opencv-图像处理之腐蚀,膨胀,二值化,加噪,模糊(滤波),轮廓提取,掩膜,梯度,卷积的推导和应用

这里我们先说图像腐蚀
图像腐蚀的原理是缩小腐蚀中心的像素值,从而达到降低图片高亮度,凸显灰暗部分的作用。
它的实现原理为我们用结构元素1去覆盖图像2中的矩阵,结构元素可以是十字形,矩形,圆形等。覆盖2后 结构元素中心像素值替换为该覆盖区域图像2中最小的值。
图像腐蚀


	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("腐蚀前", 0);//1是自动适应窗口大小,0是可调整
	imshow("腐蚀前", src1);
	Mat src2 =getStructuringElement(MORPH_CROSS, Size(9,9));//参数为(结构元素形状,结构元素矩阵大小),这里是十字形
	cout << endl<<src2 << endl;
	Mat src3;
	erode(src1, src3, src2);
	namedWindow("腐蚀后", 0);
	imshow("腐蚀后", src3);
	waitKey(0);

结构元素值如下
在这里插入图片描述
刚好是一个十字形
它的腐蚀效果如下在这里插入图片描述
我们可以观察到腐蚀后图像元素呈十字形分布,图像整体也暗了许多
而图像膨胀与腐蚀相反,是替换掉最小的值,提高亮度


	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("膨胀前", 0);//1是自动适应窗口大小,0是可调整
	imshow("膨胀前", src1);
	Mat src2 =getStructuringElement(MORPH_CROSS, Size(9,9));
	cout << endl<<src2 << endl;
	Mat src3;
	dilate(src1, src3, src2);
	namedWindow("膨胀后", 0);
	imshow("膨胀后", src3);
	waitKey(0);

在这里插入图片描述
图像高亮了许多
现在我们来说明一下图像二值化
二值化先要灰度化,也就是平均RGB颜色

	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("灰度化前", 0);//1是自动适应窗口大小,0是可调整
	imshow("灰度化前", src1);
	Mat src2;
	cvtColor(src1, src2, CV_BGR2GRAY);
	namedWindow("灰度化", 0);//1是自动适应窗口大小,0是可调整
	imshow("灰度化", src2);
	waitKey(0);

在这里插入图片描述
然后二值化,也就是按阈值的方法黑白处理,有简化图片,便于提取轮廓

Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("灰度化前", 0);//1是自动适应窗口大小,0是可调整
	imshow("灰度化前", src1);
	Mat src2;
	cvtColor(src1, src2, CV_BGR2GRAY);
	namedWindow("灰度化", 0);//1是自动适应窗口大小,0是可调整
	imshow("灰度化", src2);
	Mat src3;
	threshold(src2, src3,150, 255, 0);//二值化函数,参数为(处理前,处理后,阈值上下限,二值化的方法)其中0-5 共有6种不同的方法 可自行查阅
	namedWindow("二值化", 0);//1是自动适应窗口大小,0是可调整
	imshow("二值化", src3);
	waitKey(0);

在这里插入图片描述
在我们拍照的时候,往往受到温度,相机质量,动作稳定等影响容易产生噪点,噪点又分为椒盐噪点(看图很形象)和高斯噪点
下面我们先加椒盐噪点,原理是往图片矩阵中的元素随机黑白化

Mat salt( Mat src2, int n)//n为了保证遍历矩阵 而不是加一次盐就停止
{
	for (int k = 0; k < n; k++)//随机取矩阵数组 
	{
		int i = rand() % src2.rows;
		int j = rand() % src2.cols;
		if (src2.channels() == 1)//如果是单通道
		{
			src2.at<uchar>(i, j) = 255;		//加白盐
		}
		else//如果是三通道
		{
			src2.at<Vec3b>(i, j)[0] = 255;
			src2.at<Vec3b>(i, j)[1] = 255;
			src2.at<Vec3b>(i, j)[2] = 255;
		}
	}
	for (int k = 0; k < n; k++)
	{
		
		int i = rand() % src2.rows;
		int j = rand() % src2.cols;

		if (src2.channels() == 1)
		{
			src2.at<uchar>(i, j) = 0;		//加黑盐
		}
		else
		{
			src2.at<Vec3b>(i, j)[0] = 0;
			src2.at<Vec3b>(i, j)[1] = 0;
			src2.at<Vec3b>(i, j)[2] = 0;
		}
	}
	return src2;
}

int main()
{
	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("加盐前", 0);
	imshow("加盐前", src1);
	Mat src2 = salt(src1, 3000);
	namedWindow("加盐后", 0);
	imshow("加盐后", src2);
	//存储图像

	waitKey();
	return 0;
}

在这里插入图片描述
以及高斯噪点,高斯噪点的随机数服从正态分布,分布比较均匀

double gaosirand(double avage, double Variance)//生成高斯随机数 参数是(均值,方差)
{
	const double min = numeric_limits<double>::min();//double 最小值
	static double s1;//高斯随机数
	double s3, s4;//两个普通随机数
	do
	{
		s3 = rand() * (1.0 / RAND_MAX);//rand_max是能产生的最大随机数 这里是求0-1的随机数
		s4 = rand() * (1.0 / RAND_MAX);
	} while (s3 <= min);
	s1 = sqrt(-2.0*log(s3))*cos(2 * CV_PI*s4);//高斯随机数有两种生成方法
	//s1 = sqrt(-2.0*log(s4))*sin(2 * CV_PI*s4); //同上
	return s1 * Variance + avage;
}

Mat addgaosirand(Mat src1)
{
	Mat src2 = src1.clone();
	int channels = src1.channels();
	int rows = src1.rows;
	int cols = src1.cols*channels;// 为了一次性给一列赋值

	if (src2.isContinuous())//(矩阵是否存在行为空)
	{
		cols *= rows;//变成一列
		rows = 1;
	}
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{

			int val = src2.ptr<uchar>(i)[j] +gaosirand(2, 0.8) * 32;//高斯随机数访问数组元素,ptr是遍历元素 比at要快
			if (val < 0)
				val = 0;
			if (val > 255)
				val = 255;
			src2.ptr<uchar>(i)[j] = (uchar)val;//添加高斯噪点
		}
	}
	return src2;
}

int main()
{
	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");

	imshow("原图像", src1);
	Mat src2 = addgaosirand(src1);
	imshow("加入高斯噪声后的图像", src2);
	waitKey();
	return 0;
}

在这里插入图片描述
下面我们进行滤波去噪
滤波又叫平滑操作,也同样有多种方法,基本思想都是均值化像素,相当于把噪点像素平摊给周围像素,达到弱化噪点的作用,缺点是分摊噪点后图会“糊了”。这里我们采用均值滤波法。其算法原理是在这里插入图片描述
它的含义是取某一像素周围(k.width行,kheight列)个像素相加的平均数取代该像素

int main()
{
	Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("原图",0);
	imshow("原图", src1);
	Mat src4;
	blur(src1, src4, Size(9, 9));
	namedWindow("原图滤波", 0);
	imshow("原图滤波", src4);
	Mat src2 =imread("C://Users//Administrator//Desktop//2.jpg");
	namedWindow("椒盐噪点", 0);
	imshow("椒盐噪点", src2);
	Mat src5;
	blur(src2, src5, Size(9, 9));
	namedWindow("椒盐滤波", 0);
	imshow("椒盐滤波", src5);
	Mat src3= imread("C://Users//Administrator//Desktop//3.jpg");
	namedWindow("高斯噪点", 0);
	imshow("高斯噪点", src3);
	Mat src6;
	blur(src3, src6, Size(9, 9));
	namedWindow("高斯噪点滤波", 0);
	imshow("高斯噪点滤波", src6);
	waitKey();
	return 0;
}


掩膜mask可理解为抠图,也是与原图进行逻辑“&”运算
Canny算法的原理是设定上下阈值,<下界非边缘,>下界且<上界,又处在其他边界像素周围的一同视为边界,>上界的视为边界

Mat src1 = imread("C://Users//Administrator//Desktop//1.jpg");
	namedWindow("原图",0);
	imshow("原图", src1);
	Mat src2, src3, src4, src5;//src2是src1的灰度图,src3是src3的二值化图,src4是src3第一次提取轮廓,src5是src3第二次地区轮廓并与src1掩膜操作
	src2.create(src1.size(), src1.type());
	cvtColor(src1, src2, CV_BGR2GRAY);
	blur(src2, src4, Size(3, 3));
	Canny(src4, src4, 3, 9, 3);//边缘提取算法,上下阈值最好相差3倍,此时src4粗存的只有0和255,0是黑,255是白 其中参数为(输入图,输出边缘结果图,阈值上下限,sobe算子,默认为3*3的卷积矩阵)
	namedWindow("轮廓图", 0);
	imshow("轮廓图", src4);
	src5 = Scalar::all(0);//全黑
	src1.copyTo(src5, src4);//src1与src4掩膜给src5 其实就是src4&src1运算,0&src4.element=0,255&src4.element=src4.elementx
	namedWindow("轮廓图2", 0);
	imshow("轮廓图2", src5);//这次运算轮廓有颜色了
	waitKey();
	return 0;

在这里插入图片描述
梯度:是一个向量,物理意义是某个平面,总有一个方向(x,y,z)使得平面变化最大(也可以理解为平面最陡峭的方向),这个方向就是梯度。我们可以得知平面有无数个点,每一个点都可以朝着方向(cosa,cosb,cosy)拓展,方向导数可以表示为
在这里插入图片描述,而最大的方向导数就是梯度的方向,最大方向导数的值就是梯度的长度,该导数的方向就是梯度的方向。扩展到图中的每一个像素,梯度是图像变化最大的方向,也就是物体轮廓的边界部分。我们建立坐标系(x,y)图像可作为二维函数表示
在x方向上的导数,也就是沿着水平方向的变化
在这里插入图片描述
当h=1,也就是移动一个像素的时候得到最小梯度为f(x+1,y)-f(x,y),恰好是向左移动一个单位
在y方向上的导数,也就是沿垂直方向的变化
在这里插入图片描述当h=1,也就是移动一个像素的时候得到最小梯度为f(x,y+1)-f(x,y)向下移动一个像素
梯度的幅值指的是相邻像素的差异比较,结果为在这里插入图片描述梯度的方向角如下,方向角和幅值也能表示梯度的方向在这里插入图片描述
卷积:
假如我们有一张白纸,沿对角线卷起来在这里插入图片描述
面积也s=s1+s2+s3,用积分表示在这里插入图片描述也就是卷积积分
在这里插入图片描述在图像识别当中
原图矩阵f
在这里插入图片描述
卷积核矩阵(通常由训练得出特征值)
在这里插入图片描述
这里因为(n-i)是递减的,而矩阵的存储顺序是递增的,计算的时候我们要将它反转180°
在这里插入图片描述
我们将g矩阵以g的中心元素(这里是1)依次对准f矩阵的每一个元素。这样f矩阵就会与g矩阵整体相重合,f边界外没有行列,g无法重合,我们就计该处f的值为0,然后我们利用公式在这里插入图片描述(这里的i,j是g中的行列数,要求g反转180°)得到结果矩阵s

在这里插入图片描述

这也是卷积积分的离散表达式在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值