canny边缘检测C\C++实现

这个是主函数

#define PI              3.1415926
#define EDGE_VALUE      235
#define NON_EDGE_VALUE  16
typedef unsigned char uint8;

enum {HOR = 0, VER, POS45, NEG45};
const int row_table[4] = {0, 1, 1, 1};
const int col_table[4] = {1, 0, 1, -1};


void CannyEdge(const uint8* img, int width, int height, int pitch, 
	int low_thr, int high_thr, uint8 *edge)
{
	uint8 *lpf_img = new uint8[pitch * height];
	int *grad = new int[pitch * height];
	int *max_grad = new int[pitch * height];

	GaussianFilter(img, width, height, pitch, lpf_img);
	CalcGrad(lpf_img, width, height, pitch, grad);
	SuppressNonMax(grad, width, height, pitch, max_grad);
	GetEdge(max_grad, width, height, pitch, low_thr, high_thr, edge);

	delete [] lpf_img;
	delete [] grad;
	delete [] max_grad;
}
对图像做高斯低通滤波,这里用一个7tap的滤波器,滤波器尺寸可以根据需要做调整

二维高斯滤波可以分解成两个一维的高斯滤波,先做水平滤波,再做垂直滤波

void GaussianFilter(const uint8* srcp, int width, int height, int pitch, uint8 *dstp)
{
	int filter[7] = {1, 6, 19, 27, 19, 6, 1};
	int weight_sum = 79;
	int *fltp = filter + 3;
	uint8 *midp = new uint8[pitch * height];
	memcpy(dstp, srcp, pitch * height);
	memcpy(midp, srcp, pitch * height);

	// hor filter
	for(int ver = 0; ver < height; ++ver) {
		for(int hor = radius; hor < width - radius; ++hor) {
			int sum = 0;
			for(int x = -radius; x <= radius; ++x) {
				sum += srcp[ver * pitch + hor + x] * fltp[x];
			}
			midp[ver * pitch + hor] = sum / weight_sum;
		}
	}

	// ver filter
	for(int ver = radius; ver < height - radius; ++ver) {
		for(int hor = 0; hor < width; ++hor) {
			int sum = 0;
			for(int y = -radius; y <= radius; ++y) {
				sum += midp[(ver + y) * pitch + hor] * fltp[y];
			}
			dstp[ver * pitch + hor] = sum / weight_sum;
		}
	}

	delete [] midp;
}

计算梯度值和法线方向

void CalcGrad(uint8 *img, int width, int height, int pitch, int *grad)
{
	const double tan225 = tan(PI / 8);
	const double tan675 = tan(PI * 3 / 8);
	memset((void*)grad, 0, pitch * height * sizeof(int));

	for(int ver = 0; ver < height - 1; ++ver) {
		for(int hor = 0; hor < width - 1; ++hor) {
			int offset = ver * pitch + hor;
			int diff_x = img[offset + 1] - img[offset];
			int diff_y = img[offset + pitch] - img[offset];

			int dir;
			double tanv = diff_y / (diff_x + 0.0001);
			if(tanv < tan225 && tanv >= -tan225) dir = HOR;
			else if(tanv < tan675 && tanv >= tan225)  dir = POS45;
			else if(tanv < -tan675 || tanv >= tan675) dir = VER;
			else dir = NEG45;

			int gradient = (int)sqrt(double(diff_x * diff_x + diff_y * diff_y) );
			grad[offset] = (gradient << 2) + dir; // 低2位放方向,高位放梯度值
		}
	}
}
非最大值抑制

void SuppressNonMax(int *grad, int width, int height, int pitch, int *max_grad)
{
	memset((void*)max_grad, 0, pitch * height * sizeof(int));

	for(int ver = 1; ver < height - 1; ++ver) {
		for(int hor = 1; hor < width - 1; ++hor) {
			int offset = ver * pitch + hor;
			int dir = grad[offset] & 0x3;
			int row = row_table[dir];
			int col = col_table[dir];

			int offset1 = (ver + row) * pitch + hor + col;
			int offset2 = (ver - row) * pitch + hor - col;
			int grad0 = grad[offset] >> 2;
			int grad1 = grad[offset1] >> 2;
			int grad2 = grad[offset2] >> 2;
			if(grad0 >= grad1 && grad0 >= grad2) {
				max_grad[offset] = grad0;
			}
		}
	}
}
利用双阈值得到边缘,连接线

阈值可以根据需要进行设置,这里设置low_thr = 7,high_thr = 14

void GetEdge(int *max_grad, int width, int height, int pitch, 
	int low_thres, int high_thres, uint8 *edge)
{
	memset(edge, NON_EDGE_VALUE, pitch * height);

	for(int ver = 1; ver < height-1; ++ver) {
		for(int hor = 1; hor < width-1; ++hor) {
			int offset = ver * pitch + hor;
			if(max_grad[offset] >= high_thres) { 
				edge[offset] = EDGE_VALUE;
			}
			else if(max_grad[offset] >= low_thres) {
				for(int row = -1; row <= 1; ++row) {
					for(int col = -1; col <= 1; ++col) {
						if(max_grad[(ver + row) * pitch + hor + col] >= high_thres) {
							edge[offset] = EDGE_VALUE;
							break;
						}
					}
				}
			}
		}
	}
}


左边是原图,右边是得到的边缘


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值