【openCV】Canny边缘检测

手动实现了Canny边缘检测算法的部分步骤。
#include "opencv2/opencv.hpp"
#include <math.h>
using namespace cv;

const int dir[8][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};

inline int sqr(int x){
	return x*x;
}

void grad_low_thresholding(Mat &nms,const Mat &image,float threshold_l,int x,int y){
//在每个点周围探索,满足条件的点设为真边界,并以那个点为中心继续探索
	for (int k = 0;k < 8;k++){
		int newx = x + dir[k][0];
		int newy = y + dir[k][1];
		if (x < 0 || y < 0 || x >= nms.cols || y>=nms.rows) continue;
		if (nms.at<uchar>(newx,newy) == 128 && image.at<float>(newx,newy) >= threshold_l){
			nms.at<uchar>(newx,newy) = 255;
			grad_low_thresholding(nms,image,threshold_l,newx,newy);
		}
	}
}

int main(){
	
	// 从文件中加载原图
	Mat srcImage = imread("image.jpg");
	if(srcImage.empty())
	{//如果读入图像失败
         fprintf(stderr, "Can not load image\n");
         return 0;
    }
	
	//步骤1:高斯滤波
	Mat gauImage;
	GaussianBlur(srcImage,gauImage,Size(5,5),0,0);
	srcImage.release();
	
	//步骤2:转为灰度图 
	Mat grayImage;
	cvtColor(gauImage, grayImage, CV_BGR2GRAY);
	//cvShowImage("Original_Gray_Level_Image",grayImage);
	//cvWaitKey(0);
	//cvDestroyWindow("Original_Gray_Level_Image");
	gauImage.release();

	
	//步骤3:利用Sobel算子求x,y方向上偏导(grad_x,gray_y),再求梯度(grad)、梯度方向(theta)
	Mat grad_x,grad_y;//,abs_grad_x,abs_grad_y;
	Sobel(grayImage,grad_x,CV_32FC1,1,0);
	Sobel(grayImage,grad_y,CV_32FC1,0,1);
	//convertScaleAbs(grad_x,abs_grad_x);
	//convertScaleAbs(grad_y,abs_grad_y);

	//求梯度,角度
	//Mat grad(gauImage.rows,gauImage.cols,CV_16S),theta(gauImage.rows,gauImage.cols,CV_16S);
	Mat grad,theta;
	magnitude(grad_x,grad_y,grad);
	phase(grad_x,grad_y,theta,true);

	
	//步骤4:非极大值抑制
	//对每个点,跟其梯度方向上相邻的两个点进行比较,如果不是极大值则设为0,是则设为128。
	//处理结果存放在nms中(Non-maximum suppression)
	//Deg为梯度方向的角度值,根据角度值判断四种情况(为注释中的情况),
	//分别用g1和g2、g3和g4加上角度估算出该点在梯度方向上相邻点的梯度tmp1、tmp2,并进行比较。

	Mat nms(grad.rows,grad.cols,CV_8UC1);
	for (int j = 0;j < grad.cols;j++){
		nms.at<uchar>(0,j) = 0;
	}

	for (int i = 1; i < grad.rows-1;i++){
		nms.at<uchar>(i,0) = 0;
		for (int j = 1;j < grad.cols-1;j++){
			float deg = theta.at<float>(i,j);
			float g1,g2,g3,g4,pt;
			float weight,tmp1,tmp2;
			pt = grad.at<float>(i,j);

			//while (deg>360){deg-=360;}
			//while (deg<0){deg+=360;}
			if ( (deg >= 0 && deg < 45) || (deg >=180 && deg < 225) ){
				//***g1**tmp1**g2*************
				//****************************
				//*************pt*************
				//****************************
				//*************g3**tmp2**g4***
				g1 = grad.at<float>(i-1,j-1);
				g2 = grad.at<float>(i-1,j);
				g3 = grad.at<float>(i+1,j);
				g4 = grad.at<float>(i+1,j+1);
				weight = abs(grad_y.at<float>(i,j) / grad_x.at<float>(i,j));
				tmp1 = g1*weight + g2*(1-weight);
				tmp2 = g4*weight + g3*(1-weight);
			}
			else if ( (deg >= 45 && deg < 90) || (deg >=225 && deg < 270) ){
				//***g1***********************
				//**tmp1**********************
				//***g2********pt********g3***
				//**********************tmp2**
				//***********************g4***
				g1 = grad.at<float>(i-1,j-1);
				g2 = grad.at<float>(i,j-1);
				g3 = grad.at<float>(i,j+1);
				g4 = grad.at<float>(i+1,j+1);
				weight = abs(grad_x.at<float>(i,j) / grad_y.at<float>(i,j));
				tmp1 = g1*weight + g2*(1-weight);
				tmp2 = g4*weight + g3*(1-weight);
			}
			else if ( (deg >= 90 && deg < 135) || (deg >=270 && deg < 315) ){
				//***********************g3***
				//**********************tmp2**
				//****g1*******pt********g4***
				//****tmp1********************
				//****g2**********************
				g1 = grad.at<float>(i,j-1);
				g2 = grad.at<float>(i+1,j-1);
				g3 = grad.at<float>(i-1,j+1);
				g4 = grad.at<float>(i,j+1);
				weight = abs(grad_x.at<float>(i,j) / grad_y.at<float>(i,j));
				tmp1 = g2*weight + g1*(1-weight);
				tmp2 = g3*weight + g4*(1-weight);
			}
			else if ( (deg >= 135 && deg < 180) || (deg >=315 && deg < 360 ) ){
				//*************g1**tmp1**g2***
				//****************************
				//*************pt*************
				//****************************
				//***g3**tmp2**g4*************
				g1 = grad.at<float>(i-1,j);
				g2 = grad.at<float>(i-1,j+1);
				g3 = grad.at<float>(i+1,j-1);
				g4 = grad.at<float>(i+1,j);
				weight = abs(grad_y.at<float>(i,j) / grad_x.at<float>(i,j));
				tmp1 = g2*weight + g1*(1-weight);
				tmp2 = g3*weight + g4*(1-weight);
			}
			
			nms.at<uchar>(i,j) = ((pt >= tmp1 && pt >= tmp2)?128:0);
		}
		nms.at<uchar>(i,grad.cols-1) = 0;
	}

	for (int j = 0;j < grad.cols;j++){
		nms.at<uchar>(grad.rows-1,j) = 0;
	}
	
	imwrite("nms.jpg",nms);

	//步骤5:双阀值检测
	float threshold_h = 65;
	float threshold_l = 15;

	//首先判断高阀值,如果灰度大于高阀值,即为真边界,再在其周围根据低阀值搜索
	for (int i = 0; i < grad.rows;i++){
		for (int j = 0;j < grad.cols;j++){
			if (nms.at<uchar>(i,j) == 128 && grad.at<float>(i,j) >= threshold_h){
				nms.at<uchar>(i,j) = 255;
				grad_low_thresholding(nms,grad,threshold_l,i,j);
				//在每个点周围探索,满足条件的点设为真边界,并以那个点为中心继续探索
			}
		}
	}

	for (int i = 0; i < grad.rows;i++){
		for (int j = 0;j < grad.cols;j++){
			if (nms.at<uchar>(i,j) != 255){
				nms.at<uchar>(i,j) = 0;
			}
		}
	}

	//cvCanny(&grayImage,&nms,150,30);
	imwrite("gray.jpg",grayImage);
	imwrite("output.jpg",nms);

	grayImage.release();
	grad_x.release();
	grad_y.release();
	//abs_grad_x.release();
	//abs_grad_y.release();
	grad.release();
	theta.release();
	nms.release();
	return 0;
}


另外,openCV中有库函数cvCanny()实现了Canny边缘检测算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值