非极大值抑制
//
#include <iostream>
#include <opencv2/opencv.hpp>
cv::Mat getConvolutionKernel(cv::Size ksize, double sigam);
void GaussinaBlur(cv::Mat& input, cv::Mat& output, cv::Size ksize, double sigam);
void imagePading(cv::Mat& input, cv::Mat& output, int hight, int low, int left, int right);
void CalculatingGradient(cv::Mat& input, cv::Mat& Gradent, cv::Mat& theat, cv::Mat& dX, cv::Mat& dY);
void nmsFunction(cv::Mat grandent, cv::Mat dX, cv::Mat dY, cv::Mat& output);
void doubleThreshold(cv::Mat& input, cv::Mat& output, double lowTh, double highTh);
int main() {
cv::Mat image = cv::imread("D:\\code\\c++Code\\TestCanny\\111.png", 0);
image.convertTo(image, CV_64F);
GaussinaBlur(image, image, cv::Size(3,3), 1.5);
// 计算图像梯度
cv::Mat Gradent;
cv::Mat theat, dX, dY;
CalculatingGradient(image, Gradent, theat, dX, dY);
cv::Mat nmsImage;
nmsFunction(Gradent, dX, dY, nmsImage);
cv::Mat fImage;
doubleThreshold(nmsImage, fImage, 50, 100);
cv::normalize(nmsImage, nmsImage, 0, 255, cv::NORM_MINMAX);
nmsImage.convertTo(nmsImage, CV_8U);
//cv::convertScaleAbs(Gradent, Gradent);
cv::imshow("gaussian", nmsImage);
cv::waitKey(0);
}
// 高斯滤波
void GaussinaBlur(cv::Mat &input, cv::Mat &output, cv::Size ksize, double sigam) {
// step1 : 生成卷积核
cv::Mat convolutionKernel = getConvolutionKernel( ksize, sigam);
int width = ksize.width;
int hight = ksize.height;
int halfWidth = (width - 1) / 2;
int halfHight = (hight - 1) / 2;
int addHight = halfHight, addLow = halfHight, addLeft = halfWidth, addLight = halfWidth;
cv::Mat pimage;
imagePading(input, pimage, addHight, addLow, addLeft, addLight);
// 卷积运算
output = cv::Mat::zeros(input.size(), input.type());
if (input.channels() == 1) {
for (int i = halfHight; i < pimage.rows - halfHight; i++) {
for (int j = halfWidth; j < pimage.cols - halfWidth; j++) {
double sumA = 0;
for (int z = i - halfHight; z <= i + halfHight / 2; z++) {
for (int h = j - halfWidth; h <= j + halfWidth; h++) {
sumA += pimage.at<double>(z, h) * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
}
}
pimage.at<double>(i, j) = sumA;
}
}
}
else if (input.channels() == 3)
{
for (int i = halfHight; i < pimage.rows - halfHight; i++) {
for (int j = halfWidth; j < pimage.cols - halfWidth; j++) {
cv::Vec3d sumA = 0;
for (int z = i - halfHight; z <= i + halfHight / 2; z++) {
for (int h = j - halfWidth; h <= j + halfWidth; h++) {
sumA[0] += pimage.at<cv::Vec3d>(z, h)[0] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
sumA[1] += pimage.at<cv::Vec3d>(z, h)[1] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
sumA[2] += pimage.at<cv::Vec3d>(z, h)[2] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
}
}
pimage.at<cv::Vec3d>(i, j) = sumA;
}
}
}
output = pimage(cv::Rect(halfHight, halfWidth,input.cols, input.rows));
}
void imagePading(cv::Mat& input, cv::Mat &output, int hight, int low, int left, int right) {
// 填充0
int rows = input.rows + hight + low;
int cols = input.cols + left + right;
if (input.channels() == 1) {
output = cv::Mat::zeros(rows, cols, CV_64F);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (i >= hight && i < rows - low && j >= left && j < cols - right) {
output.at<double>(i, j) = input.at<double>(i - hight, j - left);
}
}
}
}
else if(input.channels() == 3)
{
output = cv::Mat::zeros(rows, cols, CV_64FC3);
for (int c = 0; c < input.channels(); c++) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (i >= hight && i < rows - low && j >= left && j < cols - right) {
output.at<cv::Vec3d>(i, j)[0] = input.at<cv::Vec3d>(i - hight, j - left)[0];
output.at<cv::Vec3d>(i, j)[1] = input.at<cv::Vec3d>(i - hight, j - left)[1];
output.at<cv::Vec3d>(i, j)[2] = input.at<cv::Vec3d>(i - hight, j - left)[2];
}
}
}
}
}
}
cv::Mat getConvolutionKernel(cv::Size ksize, double sigam) { // size
// 生成卷积核
cv::Mat convolutionKernel(ksize , CV_64FC1);
int width = ksize.width;
int hight = ksize.height;
int centerW = (width - 1) / 2;
int centerH = (hight - 1) / 2;
double x, y;
double sum = 0;
for (int i = 0; i < hight; i++) {
x = std::pow(i - centerH, 2);
for (int j = 0; j < width; j++) {
y = std::pow(j - centerW, 2);
double temp = std::exp(-(x + y) / (2 * std::pow(sigam, 2)));
convolutionKernel.at<double>(i, j) = temp;
sum += temp;
}
}
// 归一化
convolutionKernel = convolutionKernel / sum;
return convolutionKernel;
}
// 计算图像梯度
void CalculatingGradient(cv::Mat& input, cv::Mat& Gradent, cv::Mat& theat, cv::Mat& dX, cv::Mat& dY ){
Gradent = cv::Mat::zeros(input.size(), input.type());
theat = cv::Mat::zeros(input.size(), input.type());
dX = cv::Mat::zeros(input.size(), input.type());
dY = cv::Mat::zeros(input.size(), input.type());
//
if (input.channels() == 1) {
for (int i = 1; i < input.rows - 1; i++) {
for (int j = 1; j < input.cols - 1; j++) {
double dx2 = input.at<double>(i - 1, j + 1) + 2 * input.at<double>(i, j + 1) + input.at<double>(i + 1, j + 1) - input.at<double>(i - 1, j - 1) - 2 * input.at<double>(i, j - 1) - input.at<double>(i + 1, j - 1);
double dy2 = input.at<double>(i + 1, j - 1) + 2 * input.at<double>(i + 1, j) + input.at<double>(i + 1, j + 1) - input.at<double>(i - 1, j - 1) - 2 * input.at<double>(i - 1, j) - input.at<double>(i - 1, j + 1);
Gradent.at<double>(i, j) = std::sqrt(std::pow(dx2, 2) + std::pow(dy2, 2));
theat.at<double>(i, j) = atan(dy2 / dx2);
dX.at<double>(i, j) = dx2;
dY.at<double>(i, j) = dy2;
}
}
}
else if (input.channels() == 3) {
for (int i = 1; i < input.rows - 1; i++) {
for (int j = 1; j < input.cols - 1; j++) {
cv::Vec3d dx2 = input.at<cv::Vec3d>(i - 1, j + 1) + 2 * input.at<cv::Vec3d>(i, j + 1) + input.at<cv::Vec3d>(i + 1, j + 1) - input.at<cv::Vec3d>(i - 1, j - 1) - 2 * input.at<cv::Vec3d>(i, j - 1) - input.at<cv::Vec3d>(i + 1, j - 1);
cv::Vec3d dy2 = input.at<cv::Vec3d>(i + 1, j - 1) + 2 * input.at<cv::Vec3d>(i + 1, j) + input.at<cv::Vec3d>(i + 1, j + 1) - input.at<cv::Vec3d>(i - 1, j - 1) - 2 * input.at<cv::Vec3d>(i - 1, j) - input.at<cv::Vec3d>(i - 1, j + 1);
Gradent.at<cv::Vec3d>(i, j)[0] = std::sqrt(std::pow(dx2[0], 2) + std::pow(dy2[0], 2));
Gradent.at<cv::Vec3d>(i, j)[1] = std::sqrt(std::pow(dx2[1], 2) + std::pow(dy2[1], 2));
Gradent.at<cv::Vec3d>(i, j)[2] = std::sqrt(std::pow(dx2[2], 2) + std::pow(dy2[2], 2));
theat.at<cv::Vec3d>(i, j)[0] = atan(dy2[0] / dx2[0]);
theat.at<cv::Vec3d>(i, j)[1] = atan(dy2[1] / dx2[1]);
theat.at<cv::Vec3d>(i, j)[2] = atan(dy2[2] / dx2[2]);
}
}
}
}
// 非极大值抑制
void nmsFunction(cv::Mat grandent, cv::Mat dX, cv::Mat dY, cv::Mat &output) {
double g1 = 0, g2 = 0 ,g3 = 0, g4 = 0 ;
double wight = 0;
output = cv::Mat::zeros(grandent.size(), grandent.type());
for (int i = 1; i < grandent.rows - 1; i++) {
for (int j = 1; j < grandent.cols - 1; j++) {
if (std::abs(dY.at<double>(i, j)) > std::abs(dX.at<double>(i, j)))
{
g2 = grandent.at<double>(i - 1, j);
g3 = grandent.at<double>(i + 1, j);
wight = std::abs(dX.at<double>(i, j) /dY.at<double>(i, j)) ;
if (dY.at<double>(i, j) * dX.at<double>(i, j) > 0) { // 同号
g1 = grandent.at<double>(i - 1, j - 1);
g4 = grandent.at<double>(i + 1, j + 1);
}
else
{
g1 = grandent.at<double>(i - 1, j + 1);
g4 = grandent.at<double>(i + 1, j - 1);
}
}
else if (std::abs(dY.at<double>(i, j)) < std::abs(dX.at<double>(i, j)))
{
g2 = grandent.at<double>(i, j - 1);
g3 = grandent.at<double>(i, j + 1);
wight = std::abs(dY.at<double>(i, j) /dX.at<double>(i, j));
if (dY.at<double>(i, j) * dX.at<double>(i, j) > 0) {
g1 = grandent.at<double>(i - 1, j - 1);
g4 = grandent.at<double>(i + 1, j + 1);
}
else
{
g1 = grandent.at<double>(i + 1, j - 1);
g4 = grandent.at<double>(i - 1, j + 1);
}
}
double gA = wight * g1 + (1 - wight) * g2;
double gB = wight * g4 + (1 - wight) * g3;
if (grandent.at<double>(i, j) >= gA && grandent.at<double>(i, j) >= gB) {
output.at<double>(i, j) = grandent.at<double>(i, j);
}
}
}
}
// 双阈值
/*
大于搞阈值置为255 在阈值中间先不动
*/
void doubleThreshold(cv::Mat& input, cv::Mat& output, double lowTh, double highTh) {
output = cv::Mat::zeros(input.size(), CV_8U);
// 强边缘
for (int i = 0; i < input.rows; i++) {
for (int j = 0; j < input.cols; j++) {
if (input.at<double>(i, j) >= highTh) {
output.at<uchar>(i, j) = 255;
}
}
}
// 弱边缘
for (int i = 1; i < input.rows - 1; i++) {
for (int j = 1; j < input.cols - 1; j++) {
if (input.at<double>(i, j) < highTh && input.at<double>(i, j) >= lowTh) {
if (output.at<uchar>(i - 1, j - 1) + output.at<uchar>(i - 1, j) + output.at<uchar>(i - 1, j + 1) +
output.at<uchar>(i, j - 1) + output.at<uchar>(i, j + 1) +
output.at<uchar>(i + 1, j - 1) + output.at<uchar>(i + 1, j) + output.at<uchar>(i + 1, j + 1)
!= 0
) {
output.at<uchar>(i, j) = 255;
}
}
}
}
}