实现一个canny算子,opencv版本为3.4.5
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
/*
生成高斯卷积核 kernel
*/
void Gaussian_kernel(int kernel_size, int sigma, Mat &kernel)
{
const double PI = 3.1415926;
int m = kernel_size / 2;
kernel = Mat(kernel_size, kernel_size, CV_32FC1);
float s = 2 * sigma * sigma;
//笔记: 注意二维高斯函数下面就是2*pi*sigma^2,没有根号
for (int i = 0; i < kernel_size; i++)
{
for (int j = 0; j < kernel_size; j++)
{
int x = i - m;
int y = j - m;
kernel.at<float>(i, j) = exp(-(x * x + y * y) / s) / (PI * s);
}
}
}
/*
计算梯度值和方向
imageSource 原始灰度图
imageX X方向梯度图像
imageY Y方向梯度图像
gradXY 该点的梯度幅值
theta 梯度方向角度 theta=arctan(imageY/imageX)
*/
void GradDirection(const Mat imageSource, Mat &imageX, Mat &imageY, Mat &gradXY, Mat &theta)
{
imageX = Mat::zeros(imageSource.size(), CV_32SC1);
imageY = Mat::zeros(imageSource.size(), CV_32SC1);
gradXY = Mat::zeros(imageSource.size(), CV_32SC1);
theta = Mat::zeros(imageSource.size(), CV_32SC1);
int rows = imageSource.rows;
int cols = imageSource.cols;
/*
Mat.step参数指图像的一行实际占用的内存长度,以字节为基本单位,
因为opencv中的图像会对每行的长度自动补齐(8的倍数),
编程时尽量使用指针,指针读写像素是速度最快的,使用at函数最慢。
*/
int stepXY = imageX.step;
int step = imageSource.step;
/*
Mat::data的默认类型为uchar*,但很多时候需要处理其它类型,如float、int,
此时需要将data强制类型转换,如:
Mat src(1000,1000,CV_32F);
float* myptr = (float*)src.data;
无论Mat的type是何种类型,Mat::data均为uchar*
*/
uchar *PX = imageX.data;
uchar *PY = imageY.data;
uchar *P = imageSource.data;
uchar *XY = gradXY.data;
for (int i = 1; i < rows - 1; i++)
{
for (int j = 1; j < cols - 1; j++)
{
int a00 = P[(i - 1)*step + j - 1];
int a01 = P[(i - 1)*step + j];
int a02 = P[(i - 1)*step + j + 1];
int a10 = P[i*step + j - 1];
int a11 = P[i*step + j];
int a12