openCV学习笔记(八)--卷积,边缘处理,边缘提取,边缘检测

1.自定义线性滤波–卷积
在这里插入图片描述
卷积工作原理–即中心值根据周围定义大小邻域,从而计算成新值
在这里插入图片描述
在这里插入图片描述
常见的卷积算子:
在这里插入图片描述

//Sobel算子--获取边缘比较强,用于边缘检测
	Mat SX, SY;
	Mat sobel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
	filter2D(src, SX, -1, sobel_x, Point(-1, -1), 0.0);
	//imshow("sobel_x", SX);

	Mat sobel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
	filter2D(src, SY, -1, sobel_y, Point(-1, -1), 0.0);
	//imshow("sobel_y", SY);

	//拉普拉斯算子--获取比较完整的边缘信息
	Mat laplas;
	Mat laplas_k = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	filter2D(src, laplas,-1,laplas_k, Point(-1, -1), 0.0);
	//imshow("lap", laplas);

自定义卷积模糊:
在这里插入图片描述

//自定义卷积加深模糊--定义卷积并且随着时间而扩大结构面积,从而加大模糊程度
	Mat autoK;
	int c = 0;
	int index = 0;
	int ksize = 3;
	while (true)
	{
		c = waitKey(500);//图像变换延迟时间
		if ((char)c == 27) {//c==27-->按ESC退出
			break;
		}
		ksize = 4 + (index % 8) * 2 + 1;//卷积最小面积从5开始,随后逐渐加大
		Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);//卷积计算公式
		filter2D(src, autoK, -1, kernel, Point(-1, -1));
		index++;
		imshow("auto", autoK);

	}

2.卷积边缘处理
在这里插入图片描述
在这里插入图片描述
对于高斯模糊,默认为DEFAULT

在这里插入图片描述

	int top = (int)(0.05 * src.rows);//定义边缘扩张的值
	int bottom = (int)(0.05 * src.rows);//定义边缘扩张的值
	int left = (int)(0.05 * src.cols);//定义边缘扩张的值
	int right = (int)(0.05 * src.cols);//定义边缘扩张的值
	RNG rng(12345);//定义一个随机数种子,方便生成随机数
	int borderType = BORDER_DEFAULT;//用周围的行和列图像取填充扩张区域

	int c = 0;
	while (true)
	{
		c = waitKey(500);//等待时间
		//ESC
		if ((char)c == 27) {
			break;
		}
		if ((char)c == 'r') {
			borderType = BORDER_REPLICATE;//用边缘的颜色取插值填充
		}
		else if ((char)c == 'w') {
			borderType = BORDER_WRAP;//换行填充,即top用图片下方填充,即用另一边图像来补偿填充
		}
		else if ((char)c == 'c') {
			borderType = BORDER_CONSTANT;//用随机生成的颜色填充
		}
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//生成0-255像素值的颜色
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);//增加图像边缘
		imshow("output_Img", dst);

3. sobel算子–轮廓提取
在这里插入图片描述
在这里插入图片描述
简言之就是获取变化比较大的数据,也就是轮廓数据

在这里插入图片描述
在这里插入图片描述
相比于平方数相加再开方,绝对值相加虽然不够精确,但是可以大大减少运算时间

在这里插入图片描述
在这里插入图片描述
相比于Sobel算子,Scharr算是优化算法,可以获得更加完整的轮廓信息,并且受噪点影响较少,用法相似

具体代码实现:

//Sobel算子提取轮廓
	/*
		1.高斯模糊/平滑(sobel算子对噪声非常敏感,所以要先模糊一下)
		2.转灰度图像
		3.求x,y梯度
		4.获取最终图像(振幅图像)
	*/
	Mat gray_src;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);
	imshow("gray_Img", gray_src);

	Mat xgrad, ygrad;
	//Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);//CV_16S--输出图像深度,-1为原来深度,深度精度越大越清晰,1-x方向1阶导数,0-y不算,3-ksize
	//Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	
	//Sobel算子的改进版本--可以获取更加完整的轮廓信息,并且不会受到噪声干扰
	Scharr(gray_src, xgrad, CV_16S, 1, 0);
	Scharr(gray_src, ygrad, CV_16S, 0, 1);

	//因为用sobel算子运算后的结果可能像素为负数,所以要将结果计算绝对值,保证大于0
	convertScaleAbs(xgrad, xgrad);
	convertScaleAbs(ygrad, ygrad);
	imshow("x_img", xgrad);
	imshow("y_img", ygrad);

	//将x,y各以50%权重合并,合成完整轮廓图
	//Mat xygrad;
	//addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);//0-gamma值
	//addWeighted--相当于将两张图片的像素相加,如下所示
	Mat xygrad = Mat(xgrad.size(), xgrad.type());//定义xygrad和xgrad的大小和类型一样,防止做样
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++) {
			//处理每个像素并相加
			int xg = xgrad.at<uchar>(row, col);
			int yg = xgrad.at<uchar>(row, col);//对象.at<数据类型>(x,y)--获取对象的对应像素值
			int xy = xg + yg;//相当于两个绝对值相加(|x|+|y|),相比两个平方后相加再开放可以减少计算量((x^2+y^2)^1/2),优化了算法
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);//saturate_cast--将获取的像素值限定在0-255范围内
		}
	}

	imshow("final_Img", xygrad);

Sobel算子提取结果
在这里插入图片描述

Scharr算子提取结果
在这里插入图片描述

4.拉普拉斯算子–轮廓提取
在这里插入图片描述
在这里插入图片描述
代码:

//laplance算子--边缘提取
	//处理流程:
	//1.高斯模糊--去噪声
	//2.转换为灰度图像
	//3.拉普拉斯-二阶导数计算
	//4.取绝对值
	//5.显示结果
	Mat gray_src, edge_img;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);

	Laplacian(gray_src, edge_img, CV_16S, 3);//3-ksize
	convertScaleAbs(edge_img, edge_img);

	//因为拉普拉斯提取的噪声比较多,所以需要用切割算法,降低一些噪声
	//threshold(edge_img, edge_img, 0, 255, THRESH_OTSU | THRESH_BINARY);//THRESH_OTSU--自动取阈值,二值化切割
	namedWindow("output_img",WINDOW_AUTOSIZE);
	imshow("output_img", edge_img);

在这里插入图片描述

5.canny边缘检测算法
在这里插入图片描述

非最大信号抑制:
因为即便是sabel算子计算梯度的轮廓还是会有不止一个像素,即比较”粗“。而这个步骤则是细化边缘,见轮廓抑制为1像素,所以通过该算法将局部最大值外的所有值都置为0,保留唯一像素。
1.将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较
2.如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制
在这里插入图片描述
在这里插入图片描述

bool nonmax_suppress(double theta, Mat &g_mat, Point anchor, double *p1_v, double *p2_v)
{
        //计算8邻域灰度
	uchar N = g_mat.at<uchar>(Point(anchor.x,anchor.y + 1));
	uchar S = g_mat.at<uchar>(Point(anchor.x,anchor.y - 1));
	uchar W = g_mat.at<uchar>(Point(anchor.x - 1,anchor.y));
	uchar E = g_mat.at<uchar>(Point(anchor.x + 1,anchor.y));
	uchar NE = g_mat.at<uchar>(Point(anchor.x + 1,anchor.y + 1));
	uchar NW = g_mat.at<uchar>(Point(anchor.x - 1,anchor.y + 1));
	uchar SW = g_mat.at<uchar>(Point(anchor.x - 1,anchor.y - 1));
	uchar SE = g_mat.at<uchar>(Point(anchor.x + 1,anchor.y - 1));
	uchar M =  g_mat.at<uchar>(Point(anchor));
	double angle = theta * 360 / (2 * CV_PI);//计算角度
	//判定角度范围 计算 p1,p2插值
        if(angle > 0 && angle < 45)
	{
		*p1_v = (1- tan(theta)) * E + tan(theta) * NE;
		*p2_v = (1- tan(theta)) * W + tan(theta) * SW;
	}
	else if(angle >= 41 && angle < 90)
	{
		*p1_v = (1- tan(theta)) * NE + tan(theta) * N;
		*p2_v = (1- tan(theta)) * SW + tan(theta) * S;
	}
	else if(angle >= 90 && angle < 135)
	{
		*p1_v = (1- tan(theta)) * N + tan(theta) * NW;
		*p2_v = (1- tan(theta)) * S + tan(theta) * SE;
	}
	else
	{
		*p1_v = (1- tan(theta)) * NW + tan(theta) * W;
		*p2_v = (1- tan(theta)) * SE + tan(theta) * E;
	}
 
	if(M < *p1_v || M < *p2_v) //非最大抑制
	{
		return false;
	}
	else
		return true;
}

高低阈值输出二值图像–滞后双阈值化

在这里插入图片描述
原理
在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。
为了解决这些杂散响应,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。
如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
另一种方案是用搜索算法,通过强边缘点,搜索8领域是否存在弱边缘,如果有,以弱边缘点为中心继续搜索,直到搜索不到弱边缘截止。

	int bfs(Mat &mag, Mat &dst, int i, int j, int low)
{
	int flag = 0;
	if(dst.at<uchar>(j, i) == 0)//没有搜索过
	{
		dst.at<uchar>(j, i) = 255;//设置为255代表搜索过
		for(int n = -1; n <= 1; n++)
		{
			for(int m = -1; m <= 1; m++)
			{
				if(m == 0 && n == 0) continue;
                                //如果点在图像内,并且高于低阈值
				if(range(mag, i+n, j+m) && mag.at<uchar>(j+m, i+n) >= low)
					if(bfs(mag, dst, i+n, j+m, low))//迭代搜索直到搜索不到高于低阈值的点。
					{
						flag = 1;
						break;
 
					}
 
			}
			if(flag)
				break;
 
		}
		return 1;
	}
	return 0;
	
}

CANNY算法–API
在这里插入图片描述
L2–表示用(x^2 + y2)1/2的方法算出G
L1–直接使用绝对值相加,简化算术过程,但是没那么精确

const char* OUTPUT_TITLE = "output_title";
int t1_value = 0;//最小值
int max_value = 255;//最大值
void Canny_Demo(int, void*);

int main(int argc, char** argv) {
	src = imread("C:/Users/18929/Desktop/博客项目/项目图片/01.jpg");
	if (src.empty()) {
		printf("could not load image");
		return -1;
	}
	namedWindow("input_image", WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow("input_image", src);

	//先转换成灰度图像,再放进去操作
	cvtColor(src, gray_src,CV_BGR2GRAY);
	createTrackbar("Threshold_Value:",OUTPUT_TITLE,&t1_value,max_value,Canny_Demo);//创建一个滚动条,方便测试
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;

}
void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3),Point(-1,-1),BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2 , 3, false);//false表示用默认L1归一化

	//也可以将原本图像的颜色赋给边缘图,得到有颜色的图像
	dst.create(src.size(), src.type());//生成一张和src一样的对象
	src.copyTo(dst, edge_output);//edge_output作为Mask模板,将src的色彩给进去
	imshow(OUTPUT_TITLE, dst);
}

在这里插入图片描述

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV-Python是一个用于计算机视觉和图像处理的开源库。边缘检测OpenCV-Python中一个重要的图像处理操作。根据引用的内容,边缘检测的方法包括Laplacian边缘检测、Sobel边缘检测和Canny边缘检测。其中,Laplacian边缘检测是一种基于二阶导数的方法,Sobel边缘检测是一种基于一阶导数的方法,而Canny边缘检测是一种综合了多种方法的优化算法。您可以根据引用中的文章目录了解更多关于这些方法的详细信息。 引用给出了一个使用OpenCV-Python进行自动确定阈值的边缘检测的示例代码。该代码首先读取一张名为'bee.jpg'的图像,并将其转换为灰度图像。然后,通过应用高斯模糊对图像进行预处理,使用自动确定阈值的方法计算合适的阈值,最后使用Canny边缘检测算法检测图像的边缘。您可以根据需要调整代码中的参数来适应不同的图像。 总而言之,OpenCV-Python提供了多种边缘检测方法,包括Laplacian边缘检测、Sobel边缘检测和Canny边缘检测。您可以根据具体需求选择适合的方法,并使用OpenCV-Python提供的函数进行图像处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【OpenCV-Python】14.OpenCV边缘检测](https://blog.csdn.net/weixin_43843069/article/details/121950301)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值