ImageProcessing100Wen (Opencv C++) Q9-19

项目:

项目地址ImageProcessing100Wen极客教程地址

收集了为图像处理初学者设计的 100 个问题以及对应的理论知识。和蝾螈一起学习基本的图像处理知识,理解图像处理算法吧!

解答这里提出的问题请不要调用OpenCV的API,自己动手实践。相信对你掌握opencv有比较好的帮助。

问题 9-19:
9高斯滤波(Gaussian Filter)
10中值滤波(Median Filter)
11均值滤波器
12Motion Filter
13MAX-MIN滤波器
14差分滤波器(Differential Filter)
15Sobel滤波器
16Prewitt滤波器
17Laplacian滤波器
18Emboss滤波器
19LoG滤波器

Q9: 高斯滤波(Gaussian Filter)

问题说明:

高斯滤波器是一种可以使图像平滑的滤波器,用于去除噪声。可用于去除噪声的滤波器还有 中值滤波器平滑滤波器LoG滤波器

高斯滤波器将中心像素周围的像素按照高斯分布加权平均进行平滑化。这样的(二维)权值通常被称为卷积核(kernel)或者滤波器(filter)。

比较常用的两种 3 × 3 3\times3 3×3 5 × 5 5\times5 5×5 大小的高斯模板如下: K = 1 16   [ 1 2 1 2 4 2 1 2 1 ] ,     K = 1 273   [ 1 4 7 4 1 4 16 26 16 4 7 26 41 26 7 4 16 26 16 4 1 4 7 4 1 ] , K=\frac{1}{16}\ \left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{matrix} \right] , \ \ \ K=\frac{1}{273}\ \left[ \begin{matrix} 1 & 4 & 7 & 4 &1 \\ 4 & 16 & 26 &16 & 4 \\ 7 & 26 & 41 & 26 &7 \\ 4 & 16 & 26 &16 & 4 \\1 & 4 & 7 & 4 &1 \end{matrix} \right], K=161 121242121,   K=2731 1474141626164726412674162616414741
高斯模板中的参数是通过高斯函数计算出来的。

先看一下一维高斯分布公式( μ \mu μ 是服从正态分布的随机变量 x x x 的均值, σ \sigma σ x x x 的标准差,一般记为 0 省略)及图像(源自:高斯滤波): f ( x ) = 1 σ 2 π   e − ( x − μ ) 2 2   σ 2 f(x)=\frac{1}{\sigma\sqrt{2\pi}}\ e^{-\frac{(x-\mu)^2}{2\ \sigma^2}} f(x)=σ2π 1 e2 σ2(xμ)2

二维高斯分布公式,以及公式图像以及按图像的得出的 3 × 3 3\times3 3×3 模板均如下: g ( x , y ) = 1 2 π σ 2   e − x 2 + y 2 2   σ 2 g(x,y)=\frac{1}{2\pi\sigma^2}\ e^{-\frac{x^2+y^2}{2\ \sigma^2}} g(x,y)=2πσ21 e2 σ2x2+y2

根据计算出来的模板卷积计算图像各个点的值,归一化是保证中心点的灰度值仍处于 [ 0 , 255 ] [0,255] [0,255] 之间,如果权重和大于 1(小于 1),高斯滤波后的图像整体会偏亮(偏暗)。因为要进行归一化, 1 2 π σ 2 \frac{1}{2\pi\sigma^2} 2πσ21 可以省略掉的,在计算时可以只计算 e − x 2 + y 2 2   σ 2 e^{-\frac{x^2+y^2}{2\ \sigma^2}} e2 σ2x2+y2 的值。

由于图像的长宽不一定是滤波器大小的整数倍,所以边界处理时一般可以通过三种方式来填补缺失的周围点:

  • 对称处理,镜面对称,缺失的点用通过中心点的另一面的点的值补齐。
  • 在图像的边缘补 0 0 0,这种方法称作 Zero Padding。
  • 在图像的边缘补对应边界点的值。

在这里插入图片描述
使用高斯滤波器( 3 × 3 3\times3 3×3 大小,标准差 σ = 1.3 \sigma=1.3 σ=1.3)来对imori_noise.jpg 进行降噪处理吧!

C++实现:

先在头文件中定义了枚举类型

	//三种边界处理方式,补零,复制边缘值,取中心点对面对应点值
	enum expandedBorderMode { ZeroPadding, Replicate, Mirror };
	//高斯滤波
	Mat GaussianFilter(Mat srcImg, double sigma, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//求出模板的中心位置(原点坐标)
		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";
		int pad = floor(kernelSize / 2);
		int _x = 0, _y = 0;
		double kernel_sum = 0;
		//创建二维数组
		vector<vector<float>> kernel(kernelSize, vector<float>(kernelSize, 0));
		//计算卷积核(高斯掩膜)
		for (int y = 0; y < kernelSize; y++) {
			for (int x = 0; x < kernelSize; x++) {
				_y = y - pad;
				_x = x - pad;
				//kernel[y][x] = 1 / (2 * M_PI*sigma*sigma)*exp(-((_x*_x) + (_y * _y)) / (2 * sigma*sigma));
				kernel[y][x] = exp(-((_x * _x) + (_y * _y)) / (2 * sigma * sigma));
				kernel_sum += kernel[y][x];
			}
		}
		//归一化
		for (int y = 0; y < kernelSize; y++) {
			for (int x = 0; x < kernelSize; x++) {
				kernel[y][x] /= kernel_sum;
			}
		}
		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
	//滤波
	template <typename T>
	Mat Filtering(Mat srcImg, vector<vector<T>> kernel, expandedBorderMode mode){
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();
		int kernelSize = kernel.size();
		int pad = floor(kernelSize / 2);

		//根据输入图像的通道数决定输出图像(默认是三通道)
		Mat dstImg = Mat::zeros(height, width, CV_8UC3);
		if (channel == 1)
			dstImg = Mat::zeros(height, width, CV_8UC1);

		double val = 0;
		Mat tmpImg = ExpandedImg(srcImg, mode, pad);
		if (channel == 3) {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					for (int c = 0; c < channel; c++) {
						val = 0;
						for (int dy = -pad; dy < pad + 1; dy++) {
							for (int dx = -pad; dx < pad + 1; dx++) {
								val += (double)tmpImg.at<Vec3b>(y + pad + dy, x + pad + dx)[c] * kernel[dy + pad][dx + pad];
							}
						}
						val = fmin(fmax(0, val), 255);
						dstImg.at<Vec3b>(y, x)[c] = val;
					}
				}
			}
		}
		else {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					val = 0;
					for (int dy = -pad; dy < pad + 1; dy++) {
						for (int dx = -pad; dx < pad + 1; dx++) {
							val += (double)tmpImg.at<uchar>(y + pad + dy, x + pad + dx) * kernel[dy + pad][dx + pad];
						}
					}
					val = fmin(fmax(0, val), 255);
					dstImg.at<uchar>(y, x) = val;
				}
			}
		}
		return dstImg;
	}
	//扩展图像
	Mat ExpandedImg(Mat srcImg, expandedBorderMode mode, int pad) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();
		Mat dstImg;
		switch (mode) {
		case ZeroPadding:
			if (channel == 3) {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC3);
				//给图像及边缘赋值(四个角落+四条边+中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = 0;
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = 0;
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = 0;
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = 0;
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = 0;
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<Vec3b>(y + pad, x + pad) = srcImg.at<Vec3b>(y, x);
					}
				}
			}
			else {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC1);
				//给图像及边缘赋值(四个角落+四条边+中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = 0;
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = 0;
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<uchar>(y + pad, x) = 0;
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y + pad, x) = 0;
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<uchar>(y, x + pad) = 0;
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<uchar>(y, x + pad) = 0;
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<uchar>(y + pad, x + pad) = srcImg.at<uchar>(y, x);
					}
				}
			}
			break;
		case Replicate:
			if (channel == 3) {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC3);
				//给图像及边缘赋值(四个角落+四条边+中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(0,0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(0, width - 1);
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(height - 1, 0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(height - 1, width - 1);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = srcImg.at<Vec3b>(y, 0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = srcImg.at<Vec3b>(y, width - 1);
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = srcImg.at<Vec3b>(0, x);
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = srcImg.at<Vec3b>(height - 1, x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<Vec3b>(y + pad, x + pad) = srcImg.at<Vec3b>(y, x);
					}
				}
			}
			else {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC1);
				//给图像及边缘赋值(四个角落+四条边+中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(0, 0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(0, width - 1);
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(height - 1, 0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(height - 1, width - 1);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<uchar>(y + pad, x) = srcImg.at<uchar>(y, 0);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y + pad, x) = srcImg.at<uchar>(y, width - 1);
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<uchar>(y, x + pad) = srcImg.at<uchar>(0, x);
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<uchar>(y, x + pad) = srcImg.at<uchar>(height - 1, x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<uchar>(y + pad, x + pad) = srcImg.at<uchar>(y, x);
					}
				}
			}
			break;
		case Mirror:
			if (channel == 3) {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC3);
				//给图像及边缘赋值(四个角落 + 四条边 + 中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(pad - y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(pad - y, 2 * width + 1 - x);
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(2 * height + 1 - y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y, x) = srcImg.at<Vec3b>(2 * height + 1 - y, 2 * width + 1 - x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = srcImg.at<Vec3b>(y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<Vec3b>(y + pad, x) = srcImg.at<Vec3b>(y, 2 * width + 1 - x);
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = srcImg.at<Vec3b>(pad - y, x);
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<Vec3b>(y, x + pad) = srcImg.at<Vec3b>(2 * height + 1 - y, x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<Vec3b>(y + pad, x + pad) = srcImg.at<Vec3b>(y, x);
					}
				}
			}
			else {
				dstImg = Mat::zeros(height + 2 * pad, width + 2 * pad, CV_8UC1);
				//给图像及边缘赋值(四个角落 + 四条边 + 中间区域)
				for (int y = 0; y < pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(pad - y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(pad - y, 2 * width + 1 - x);
				}
				for (int y = height + pad; y < height + 2 * pad; ++y) {
					for (int x = 0; x < pad; ++x) 
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(2 * height + 1 - y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y, x) = srcImg.at<uchar>(2 * height + 1 - y, 2 * width + 1 - x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < pad; ++x)
						dstImg.at<uchar>(y + pad, x) = srcImg.at<uchar>(y, pad - x);
					for (int x = width + pad; x < width + 2 * pad; ++x)
						dstImg.at<uchar>(y + pad, x) = srcImg.at<uchar>(y, 2 * width + 1 - x);
				}
				for (int x = 0; x < width; ++x) {
					for (int y = 0; y < pad; ++y)
						dstImg.at<uchar>(y, x + pad) = srcImg.at<uchar>(pad - y, x);
					for (int y = height + pad; y < height + 2 * pad; ++y)
						dstImg.at<uchar>(y, x + pad) = srcImg.at<uchar>(2 * height + 1 - y, x);
				}
				for (int y = 0; y < height; ++y) {
					for (int x = 0; x < width; ++x) {
						dstImg.at<uchar>(y + pad, x + pad) = srcImg.at<uchar>(y, x);
					}
				}
			}
			break;
		default:
			break;
		}
		return dstImg;
	}
测试及结果:
int main(){
	//高斯滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = GaussianFilter(image, 1.3, 3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q10: 中值滤波(Median Filter)

问题说明:

中值滤波是基于排序统计理论的一种非线性信号处理技术,基本原理是在数字图像或数字序列中,把某一点的值用其邻域中各点值的中值代替,从而消除孤立的噪声点。

使用中值滤波器( 3 × 3 3\times3 3×3大小)来对imori_noise.jpg进行降噪处理吧!

中值滤波器是一种可以使图像平滑的滤波器。这种滤波器用滤波器范围内(在这里是 3 × 3 3\times3 3×3)像素点的中值进行滤波,请在这里也采用 Zero Padding。

C++实现:
	//中值滤波
	Mat MedianFilter(Mat srcImg, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();
		
		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";
		int pad = floor(kernelSize / 2);

		//根据输入图像的通道数决定输出图像(默认是三通道)
		Mat dstImg = Mat::zeros(height, width, CV_8UC3);
		if (channel == 1)
			dstImg = Mat::zeros(height, width, CV_8UC1);

		vector<int> kernelVals(kernelSize*kernelSize, 0);

		if (mode == NULL)
			mode = ZeroPadding;

		Mat tmpImg = ExpandedImg(srcImg, mode, pad);

		int i = 0;
		if (channel == 3) {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					for (int c = 0; c < channel; c++) {
						i = 0;
						for (int dy = -pad; dy < pad + 1; dy++) {
							for (int dx = -pad; dx < pad + 1; dx++) {
								kernelVals[i++] = tmpImg.at<Vec3b>(y + pad + dy, x + pad + dx)[c];
							}
						}
						sort(kernelVals.begin(), kernelVals.end());
						dstImg.at<Vec3b>(y, x)[c] = kernelVals[pad+1];
					}
				}
			}
		}
		else {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					i = 0;
					for (int dy = -pad; dy < pad + 1; dy++) {
						for (int dx = -pad; dx < pad + 1; dx++) {
							kernelVals[i++] = tmpImg.at<uchar>(y + pad + dy, x + pad + dx);
						}
					}
					sort(kernelVals.begin(), kernelVals.end());
					dstImg.at<uchar>(y, x) = kernelVals[pad + 1];
				}
			}
		}
		return dstImg;
	}
测试及结果:
int main(){
	//中值滤波
	Mat imsage = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = MedianFilter(image, 3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q11: 均值滤波器

问题说明:

均值滤波也称为线性滤波,主要采用邻域平均法,基本原理是用某一点邻域值的均值代替原图像中点的像素值。

使用 3 × 3 3\times3 3×3 的均值滤波器来进行滤波吧!

C++实现:
	//均值滤波
	Mat MeanFilter(Mat srcImg, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";
		int pad = floor(kernelSize / 2);

		//根据输入图像的通道数决定输出图像(默认是三通道)
		Mat dstImg = Mat::zeros(height, width, CV_8UC3);
		if (channel == 1)
			dstImg = Mat::zeros(height, width, CV_8UC1);

		double sumVals = 0;

		if (mode == NULL)
			mode = ZeroPadding;

		Mat tmpImg = ExpandedImg(srcImg, mode, pad);
		if (channel == 3) {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					for (int c = 0; c < channel; c++) {
						sumVals = 0;
						for (int dy = -pad; dy < pad + 1; dy++) {
							for (int dx = -pad; dx < pad + 1; dx++) {
								sumVals += tmpImg.at<Vec3b>(y + pad + dy, x + pad + dx)[c];
							}
						}
						dstImg.at<Vec3b>(y, x)[c] = sumVals / (kernelSize*kernelSize);
					}
				}
			}
		}
		else {
			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					sumVals = 0;
					for (int dy = -pad; dy < pad + 1; dy++) {
						for (int dx = -pad; dx < pad + 1; dx++) {
							sumVals += tmpImg.at<uchar>(y + pad + dy, x + pad + dx);
						}
					}
					dstImg.at<uchar>(y, x) = sumVals / (kernelSize*kernelSize);
				}
			}
		}
		return dstImg;
	}
测试及结果:
int main(){
	//均值滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = MeanFilter(image, 3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q12: Motion Filter

问题说明:

运动模糊滤波取对角线方向上像素的平均值,像下式这样定义:
[   1 3   0 0 0   1 3   0 0 0   1 3   ] \left[ \begin{matrix} \ \frac{1}{3}\ & 0 & 0 \\ 0 & \ \frac{1}{3}\ & 0 \\ 0 & 0 & \ \frac{1}{3}\ \end{matrix} \right]  31 000 31 000 31 使用 3 × 3 3\times3 3×3 的Motion Filter来进行滤波吧。

C++实现:
	//运动模糊滤波
	Mat MotionFilter(Mat srcImg, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";

		//创建二维数组
		vector<vector<float>> kernel(kernelSize, vector<float>(kernelSize, 0));
		//计算卷积核(左上至右下斜对角线)
		for (int x = 0; x < kernelSize; x++) {
			kernel[x][x] = 1.0 * 1 / kernelSize;
		}

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//运动模糊滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = MotionFilter(image, 3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q13: MAX-MIN滤波器

问题说明:

使用MAX-MIN滤波器来进行滤波吧。

MAX-MIN滤波器计算出网格内像素最大值和最小值的差值,来对网格内像素重新赋值,通常用于边缘检测(检测图像中的线)。像这样提取图像中的信息的操作被称为特征提取。

边缘检测通常在灰度图像上进行。

C++实现:
	//Max-Min滤波
	Mat MaxMinFilter(Mat srcImg, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";
		int pad = floor(kernelSize / 2);

		//如果输入彩色图像,转化为灰度图
		Mat dstImg = Mat::zeros(height, width, CV_8UC1);
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		vector<int> kernelVals(kernelSize*kernelSize, 0);

		if (mode == NULL)
			mode = ZeroPadding;

		Mat tmpImg = ExpandedImg(srcImg, mode, pad);

		int i = 0;
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				i = 0;
				for (int dy = -pad; dy < pad + 1; dy++) {
					for (int dx = -pad; dx < pad + 1; dx++) {
						kernelVals[i++] = tmpImg.at<uchar>(y + pad + dy, x + pad + dx);
					}
				}
				sort(kernelVals.begin(), kernelVals.end());
				dstImg.at<uchar>(y, x) = kernelVals[kernelSize*kernelSize - 1] - kernelVals[0];
			}
		}
		return dstImg;
	}
测试及结果:
int main(){
	//运动模糊滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = MaxMinFilter(image, 3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
    return 0;
}
图片替换文本

Q14: 差分滤波器(Differential Filter)

问题说明:

使用 3 × 3 3\times3 3×3 的差分滤波器来进行滤波吧。

差分滤波器对图像亮度急剧变化的边缘有提取效果,可以获得邻接像素的差值。
纵 向 : [ 0 − 1 0 0 1 0 0 0 0 ]         横 向 : [ 0 0   0 − 1 1   0 0 0   0 ] 纵向: \left[ \begin{matrix} 0 & -1 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 0 \end{matrix} \right] \ \ \ \ \ \ \ 横向:\left[ \begin{matrix} 0 & 0 & \ 0 \\ -1 & 1 & \ 0 \\ 0 & 0 & \ 0 \end{matrix} \right] 000110000       010010 0 0 0

C++实现:

头文件声明中默认是水平方向滤波器。

	//差分滤波器
	Mat DifferentialFilter(Mat srcImg, bool isHorizontal, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//如果输入彩色图像,转化为灰度图
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		//创建二维数组,计算卷积核
		vector<vector<int>> kernel{ {0,0,0},{-1,1,0},{0,0,0} };
		if (!isHorizontal) {
			kernel[0][1] = -1;
			kernel[1][0] = 0;
		}

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//差分滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = DifferentialFilter(image);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q15: Sobel滤波器

问题说明:

使用 3 × 3 3\times3 3×3 的Sobel滤波器来进行滤波吧。

Sobel滤波器可以提取特定方向(纵向或横向)的边缘,滤波器按下式定义:
纵 向 : [ 1 2 1 0 0 0 − 1 − 2 − 1 ]         横 向 : [ 1    0 − 1 2    0 − 2 1    0 − 1 ] 纵向: \left[ \begin{matrix} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{matrix} \right] \ \ \ \ \ \ \ 横向:\left[ \begin{matrix} 1 & \ \ 0 & -1 \\ 2 & \ \ 0 & -2 \\ 1 & \ \ 0 & -1 \end{matrix} \right] 101202101       121  0  0  0121略微展开的话,尺寸计算可以看看这里:不同尺寸的Sobel模板 ,尺寸必须是 1,3,5 或 7(尺寸为 1 时,使用 3 × 1 3\times1 3×1 1 × 3 1\times3 1×3 内核 ,不进行高斯平滑操作)以及可以通过旋转卷积核(0° 45° 90° 135° … \dots 360°)来扩展。

C++实现:

头文件声明中默认是水平方向滤波器。

	//Sobel滤波器
	Mat SobelFilter(Mat srcImg, bool isHorizontal, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//如果输入彩色图像,转化为灰度图
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		//创建二维数组,计算卷积核
		vector<vector<int>> kernel{ {1,0,-1},{2,0,-2},{1,0,-1} };
		if (!isHorizontal) {
			kernel[0][1] = 2;
			kernel[0][2] = 1;
			kernel[1][0] = 0;
			kernel[1][2] = 0;
			kernel[2][0] = -1;
			kernel[2][1] = -2;
		}

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//Sobel滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = SobelFilter(image);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q16: Prewitt滤波器

问题说明:

使用 3 × 3 3\times3 3×3 的Prewitt滤波器来进行滤波吧。

Prewitt滤波器是用于边缘检测的一种滤波器,使用下式定义:
纵 向 : [ − 1 − 1 − 1 0 0 0 1 1 1 ]         横 向 : [ − 1    0    1 − 1    0    1 − 1    0    1 ] 纵向: \left[ \begin{matrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1 & 1 & 1 \end{matrix} \right] \ \ \ \ \ \ \ 横向:\left[ \begin{matrix} -1 & \ \ 0 & \ \ 1 \\ -1 & \ \ 0 & \ \ 1 \\ -1 & \ \ 0 & \ \ 1 \end{matrix} \right] 101101101       111  0  0  0  1  1  1感觉数值上和差分有点像,形式上和Sobel类似差不多。

C++实现:
	//Prewitt滤波器
	Mat PrewittFilter(Mat srcImg, bool isHorizontal, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//如果输入彩色图像,转化为灰度图
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		//创建二维数组,计算卷积核
		vector<vector<int>> kernel{ {-1,0,1},{-1,0,1},{-1,0,1} };
		if (!isHorizontal) {
			kernel[0][1] = -1;
			kernel[0][2] = -1;
			kernel[1][0] = 0;
			kernel[1][2] = 0;
			kernel[2][0] = 1;
			kernel[2][1] = 1;
		}

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//Prewitt滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	Mat img = PrewittFilter(image);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q17: Laplacian滤波器

问题说明:

Laplacian滤波器是一种二阶导数算子,具有旋转不变性,可以满足不同方向的图像边缘锐化的要求。通常情况下,其算子的系数之和需要为零,如下: [ 0 1 0 1 − 4 1 0 1 0 ] \left[ \begin{matrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{matrix} \right] 010141010

C++实现:
	Mat LaplacianFilter(cv::Mat srcImg, expandedBorderMode mode){
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//如果输入彩色图像,转化为灰度图
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		//创建二维数组,计算卷积核
		vector<vector<int>> kernel{ {0,1,0},{1,-4,1},{0,1,0} };

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//LaplacianFilter滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	Mat img = LaplacianFilter(image);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q18: Emboss滤波器

问题说明:

Emboss滤波器常用于检测图像的边缘和轮廓,能够有效地增强图像的高频信息(边缘和轮廓),并保留图像的低频信息(图像内容)。
[ − 2 − 1 0 − 1 1 1 0 1 2 ] \left[ \begin{matrix} -2 & -1 & 0 \\ -1 & 1 & 1 \\ 0 & 1 & 2 \end{matrix} \right] 210111012

C++实现:
	//Emboss滤波器
	Mat EmbossFilter(cv::Mat srcImg, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//如果输入彩色图像,转化为灰度图
		if (channel == 3)
			srcImg = BGR2GRAY(srcImg);

		//创建二维数组,计算卷积核
		vector<vector<int>> kernel{ {-2,-1,0},{-1,1,1},{0,1,2} };

		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//Emboss滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	Mat img = EmbossFilter(image);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本

Q19: LoG滤波器

问题说明:

LoG即高斯-拉普拉斯(Laplacian of Gaussian)的缩写,使用高斯滤波器使图像平滑化之后再使用拉普拉斯滤波器使图像的轮廓更加清晰。

为了防止拉普拉斯滤波器计算二次微分会使得图像噪声更加明显,所以我们首先使用高斯滤波器来抑制噪声。

LoG 滤波器使用以下式子定义: L O G ( x , y ) = x 2 + y 2 − σ 2 2 π σ 6   e − x 2 + y 2 2   σ 2 LOG(x,y)=\frac{x^2+y^2-\sigma^2}{2\pi\sigma^6}\ e^{-\frac{x^2+y^2}{2\ \sigma^2}} LOG(x,y)=2πσ6x2+y2σ2 e2 σ2x2+y2

C++实现:
	//LoG滤波器
	Mat LoGFilter(Mat srcImg, double sigma, int kernelSize, expandedBorderMode mode) {
		int width = srcImg.cols;
		int height = srcImg.rows;
		int channel = srcImg.channels();

		//求出模板的中心位置(原点坐标)
		if (kernelSize % 2 == 0)
			cout << "错误!模板尺寸应该是奇数。";
		int pad = floor(kernelSize / 2);
		int _x = 0, _y = 0;
		double kernel_sum = 0;
		//创建二维数组
		vector<vector<double>> kernel(kernelSize, vector<double>(kernelSize, 0));
		//计算卷积核(高斯掩膜)
		for (int y = 0; y < kernelSize; y++) {
			for (int x = 0; x < kernelSize; x++) {
				_y = y - pad;
				_x = x - pad;
				kernel[y][x] = ((_x*_x) + (_y * _y) - (sigma*sigma)) /
					(2 * M_PI*pow(sigma, 6))*exp(-((_x*_x) + (_y * _y)) / (2 * sigma*sigma));
				kernel_sum += kernel[y][x];
			}
		}
		//归一化
		for (int y = 0; y < kernelSize; y++) {
			for (int x = 0; x < kernelSize; x++) {
				kernel[y][x] /= kernel_sum;
			}
		}
		//滤波(掩膜*图像)
		if (mode == NULL)
			mode = ZeroPadding;
		Mat dstImg = Filtering(srcImg, kernel, mode);
		return dstImg;
	}
测试及结果:
int main(){
	//LoG滤波
	Mat image = imread("..\\Resource\\imori.jpg");
	//image = BGR2GRAY(image);
	Mat img = LoGFilter(image,5,3);
	namedWindow("sample", 0);
	imshow("sample", img);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
图片替换文本
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值