OpenCV的边缘计算

OpenCV的边缘计算

三月 22, 2022

基于梯度的检测器

梯边缘,顶边缘,坡边缘

有限差分

最简单的梯度算子
$$
gx=f(x+1,y)-f(x,y) \
gy=f(x,y+1)-f(x,y)
$$

Sobel算子

对于具有噪声的图有很好的效果
$$
gx=\frac{1}{8}\left[ \begin{matrix} -1 & 0 & 1 \ -2 & 0 & 2 \ -1 & 0 & 1 \end{matrix} \right]\
gy=\frac{1}{8}\left[ \begin{matrix} 1 & 2 & 1 \ 0 & 0 & 0 \ -1 & -2 & -1 \end{matrix} \right]
$$

基于曲率的检测器

缺点,对于薄边缘在精准定位边缘没有优势

Marr-Hildreth

$$
gx-g(x-1)=f(x+1,y)-f(x,y) -(f(x,y)-f(x-1,y) )=f(x+1,y)-f(x-1,y)-2f(x,y)
$$

高斯的拉普拉斯滤波器LoG

考虑四边的核
$$
\frac{1}{8}\left[ \begin{matrix} 0 & -1 & 0 \ -1 & 4 & -1 \ 0 & -1 & 0 \end{matrix} \right]
$$
考虑全部八个方向
$$
\frac{1}{8}\left[ \begin{matrix} -1 & -1 & -1 \ -1 & 8 & -1 \ -1 & -1 & -1 \end{matrix} \right]
$$

Canny边缘检测

canny的四步法

  1. 使用低通滤波器抑制噪声

  2. 计算梯度幅度和方向图

  3. 对梯度幅度图使用非极大值抑制

  4. 运用迟滞和连通分析检测边缘

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <opencv2/core/core.hpp>
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main() {
    Mat srcImage, grayImage;

    srcImage = imread("Lena.png");
    imshow("Lena.png", srcImage);
    Mat srcImage1 = srcImage.clone();
    //图形转换:
    cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
    Mat dstImage, edge;
    //均值滤波
    blur(srcImage1, grayImage, Size(3, 3));



    imshow("grayImage.png", grayImage);

    GaussianBlur(grayImage, grayImage, Size(5, 5), 0.8);
    /*
    边缘检测
    低于阈值1的像素点会被认为不是边缘;
    高于阈值2的像素点会被认为是边缘;
    在阈值1和阈值2之间的像素点,若与第2步得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘。
    */
    Canny(grayImage, edge, 150, 100, 3);

    dstImage.create(srcImage1.size(), srcImage1.type());
    dstImage = Scalar::all(0);
    /*
    image.copyTo(imageROI)。作用是把image的内容复制粘贴到imageROI上;
    image.copyTo(imageROI,mask)。 作用是把mask和image重叠以后把mask中像素值为0(black)的点对应的image中的点变为透明,而保留其他点。
    */
    srcImage1.copyTo(dstImage, edge);
    imwrite("canny.jpg", dstImage);
    imshow("canny.jpg", dstImage);
    waitKey(0);

    return 0;
    }

fast算子

参考这个代码

计算步骤

  1. 从图片中选取一个坐标点,获取该点的像素值,接下来判定该点是否为特征点.

  2. 选取一个以选取点坐标为圆心的半径等于三的Bresenham圆(一个计算圆的轨迹的离散算法,得到整数级的圆的轨迹点),一般来说,这个圆上有16个点

  3. 现在选取一个阈值,假设为t,关键步骤,假设这16个点中,有N个连续的像素点,他们的亮度值与中心点的像素值的差大于或者小于t,那么这个点就是一个特征点.(n的取值一般取值12或者9,实验证明9可以取得更好的效果,因为可以获取更多的特征点,后面进行处理时,数据样本额相对多一些).

  4. 加入每个轨迹点都需要遍历的话,那么需要的时间比较长,有一种比较简单的方法可以选择,那就是仅仅检查在位置1,9,5和13四个位置的像素,首先检测位置1和位置9,如果它们都比阈值暗或比阈值亮,再检测位置5和位置13,该满足判断条件。如果不满足中心点不可能是一个角点。对于所有点做上面这一部分初步的检测后,符合条件的将成为候选的角点,我们再对候选的角点,做完整的测试,即检测圆上的所有点.

但是,这种检测方法会带来一个问题,就是造成特征点的聚簇效应,多个特征点在图像的某一块重复高频率的出现,FAST算法提出了一种非极大值抑制的办法来消除这种情况,具体办法如下

  1. 对所有检测到的角点构建一个打分函数。就是像素点与周围16个像素点差值的绝对值之和。

  2. 考虑两个相邻的角点,并比较它们的值。

  3. 值较低的角点将会被删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat srcImage, grayImage;

srcImage = imread("Lena.png");
imshow("Lena.png", srcImage);

Mat gray;
cvtColor(srcImage, gray, COLOR_BGR2GRAY);

Mat LenaFast;
vector<KeyPoint>detectKeyPoint;
Mat keyPointImage1;
Ptr<FastFeatureDetector> fast = FastFeatureDetector::create();
fast->detect(gray, detectKeyPoint);
drawKeypoints(srcImage, detectKeyPoint, keyPointImage1, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("keyPoint image1", keyPointImage1);
//imshow("keyPoint image2", keyPointImage2);
waitKey(0);
return 0;

}
1
2
3
4
5
6
7
8
9
10
11
enum struct DrawMatchesFlags
{
DEFAULT = 0, //!< 将创建输出图像矩阵(Mat::create),
//!< i.e. 输出图像的现有内存可以重用.
//!< 将绘制两个源图像,匹配和单个关键点.
//!< 对于每个关键点,只有中心点将被绘制(没有带关键点大小和方向的圆点)
DRAW_OVER_OUTIMG = 1, //!< 输出图像矩阵将不会被创建 (Mat::create).
//!< 匹配将绘制在输出图像的现有内容上.
NOT_DRAW_SINGLE_POINTS = 2, //!< 不会绘制单个关键点。
DRAW_RICH_KEYPOINTS = 4 //!< 对于每个关键点,将绘制具有关键点大小和方向的圆。
};

Harris算子

步骤如下:

  1. 生成梯度图像gx,gy

  2. 一个颜色块A,A1到A9 3x3,一个颜色块B,B1到B9 3x3 w x(Ai-Bi)的平方。w是权值比如A5到Ai的距离。

  3. 得到两个特征值下x,y如果两个值都很小,没有特征。如果一个很大就存在一条边,都很大,这个点有一个角点。

c++的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
Mat srcImage, grayImage;

srcImage = imread("Lena.png");
imshow("Lena.png", srcImage);

Mat gray;
cvtColor(srcImage, gray, COLOR_BGR2GRAY);

Mat LenaHarris;
cornerHarris(gray, LenaHarris, 7,7, 0.1);
imshow("LenaHarris.jpg", LenaHarris);
waitKey(0);
return 0;

}