边缘检测原理
图像的边缘指的是图像中像素灰度值突然发生变化的区域,如果将图像的每一行像素和每一列像素都描述成一个关于灰度值的函数,那么图像的边缘对应在灰度值函数中是函数值突然变大的区域。函数值的变化趋势可以用函数的导数描述。当函数值突然变大时,导数也必然会变大,而函数值变化较为平缓区域,导数值也比较小,因此可以通过寻找导数值较大的区域去寻找函数中突然变化的区域,进而确定图像中的边缘位置。图给出一张含有边缘的图像,图像每一行的像素灰度值变化可以用图中下方的曲线表示。
通过像素灰度值曲线可以看出图像边缘位于曲线变化最陡峭的区域,对灰度值曲线求取一阶导数可以得到图中所示的曲线,通过曲线可以看出曲线的最大值区域就是图像中的边缘。
由于图像是离散的信号,我们可以用临近的两个像素差值来表示像素灰度值函数的导数,求导形式可以用式来表示。
图像的边缘有可能是由高像素值变为低像素值,也有可能是由低像素值变成高像素值,通过式得到的正数值表示需要像素值突然由低变高,得到的负数值表示像素值由高到低,这两种都是图像的边缘,因此为了在图像中同时表示出这两种边缘信息,需要将计算的结果求取绝对值。
OpenCV 中提供了convertScaleAbs()函数用计算矩阵中所有数据的绝对值
void convertScaleAbs(InputArray src, OutputArray dst,double alpha = 1, double beta = 0);
- src:输入矩阵。
- dst:计算绝对值后输入矩阵。
- alpha:缩放因子,默认参数为只求取绝对值不进行缩放。
- beta:在原始数据上添加的偏值,默认参数表示不增加偏值。
该函数可以求取矩阵中所有数据的绝对值。函数前两个参数分别为输入、输出矩阵,两个参数可以是相同的变量。函数第三个和第四个参数为对绝对值的缩放和原始数据上的偏移。
图像的边缘包含X方向的边缘和Y方向的边缘,因此分别求取两个方向的边缘后,对两个方向的边缘求取并集就是整幅图像的边缘,即将图像两个方向边缘结果相加得到整幅图像的边缘信息。
简单示例
//
// Created by smallflyfly on 2021/6/15.
//#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "utils.hpp"#include <iostream>using namespace std;
using namespace cv;int main() {Mat im = imread("test.jpg", IMREAD_GRAYSCALE);if (im.empty()) {cerr << "image file read error" << endl;return -1;}resize(im, im, Size(0,0), 0.5, 0.5);Mat kernel1 = (Mat_<float>(1, 2) << 1, -1);Mat kernel2 = (Mat_<float>(1, 3) << 1, 0, -1);Mat kernel3 = (Mat_<float>(3, 1) << 1, 0, -1);Mat kernelXY = (Mat_<float>(2, 2) << 1, 0, 0, -1);Mat kernelYX = (Mat_<float>(2, 2) << 0, -1, 1, 0);// 边缘检测Mat result1, result2, result3, resultXY, resultYX;// 检测水平方向边缘filter2D(im, result1, -1, kernel1);convertScaleAbs(result1, result1);// 检测水平方向边缘filter2D(im, result2, -1, kernel2);convertScaleAbs(result2, result2);// 检测垂直方向边缘filter2D(im, result3, -1, kernel3);convertScaleAbs(result3, result3);// 整幅图像的边缘Mat result23 = result2 + result3;// 检测左上到右下的边缘filter2D(im, resultXY, -1, kernelXY);convertScaleAbs(resultXY, resultXY);// 检测右上到左下的边缘filter2D(im, resultYX, -1, kernelYX);convertScaleAbs(resultYX, resultYX);Mat resultXYYX = resultXY + resultYX;showImage("result1", result1);showImage("result2", result2);showImage("result3", result3);showImage("result23", result23);showImage("resultXY", resultXY);showImage("resultYX", resultYX);showImage("resultXYYX", resultXYYX);waitKey(0);destroyAllWindows();return 0;}
结果图