图像分割

Author Avatar
Euan 11月 21, 2019
  • 在其它设备中阅读本文章

图像分割

实验目的

掌握基于openCV的图像分割方法,理解图像分割的处理

实验原理

边缘检测方法、霍尔变换

实验内容或步骤

对“lena.jpg”及“车牌.jpg” 的灰度图像分别进行如下操作:

1)先平滑滤波,进行Canny边缘检测[尝试使用不同参数,调试效果],显示并合并为一张图像保存;

Canny算法所采用的方法

在本文实现的Canny算法中所采用的卷积算子比较简单,表达如下:

1

其x向、y向的一阶偏导数矩阵,梯度幅值以及梯度方向的数学表达式为:

2

求出这几个矩阵后,就可以进行下一步的检测过程。

Canny算法介绍-五步:

  • 高斯模糊
  • 灰度计算
  • 计算梯度
  • 非最大信号抑制
  • 高低阈值输出二值图像

首先使用高斯滤波去除一定的噪声,后进行canny边缘检测。

MEAAc6.png

可以看到图像平滑了挺多

MEAEjK.png

MEAk1x.png

使用滑动条不断调节得到边缘检测最佳效果,记录参数后把canny参数设置为定值

2)在1)的基础上,进行Hough直线变换(尝试使用不同参数,调试效果) ,描绘显示并合并为一张图像保存。

参考:

canny

HoughLines

实验报告

HoughLines( )函数详解

此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。

C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )

  • 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
  • 第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,是离坐标原点((0,0)(也就是图像的左上角)的距离。 是弧度线条旋转角度(0垂直线,π/2水平线)。
  • 第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
  • 第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • 第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • 第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
  • 第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。

3

1、初始化(θ,p)空间,N(θ,p)=0 (N(θ,p)表示在该参数表示的直线上的像素点的个数)

2、对于每一个像素点(x,y),在参数空间中找出令 xcosθ+ysinθ=p 的(θ,p)坐标,N(θ,p)+=1

3、统计所有N(θ,p)的大小,取出N(θ,p)>threasold的参数 (threadsold是预设的阈值)

MEAZnO.png

MEAeBD.png

MEAmHe.png

当你使用滑动条来改变 阈值 的时候会观察到检测到线的数目的改变. 这是因为: 如果你设置了一个更大的阈值, 能检测到的线的数目将更少 (你需要更多的点来表示一条能检测到的直线).

使用滑动条不断调节得到边直线测最佳效果,记录参数后把houghlines参数设置为定值,但一开始没起作用,一片红,可能for循环导致了不能随时更换,后来直接把变量设置为三个定值(80,100,120),得到直线检测的效果

这次实验的边缘检测想过不太理想,没有很精确,可能是因为边缘检测的效果设置太严格,亦可能是houghline参数设置得不够精确

源代码

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<fstream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;
#define WindowNameSrc "canny_car" //定义窗口名字的宏
#define WindowNameImg "canny_lena" //定义窗口名字的宏
#define WindowNameHoughSrc "hough_car" //定义窗口名字的宏
#define WindowNameHoughImg "hough_lena" //定义窗口名字的宏

char Trackbarname1[50]; //用于存储滑动条1的名称
char Trackbarname2[50]; //用于存储滑动条2的名称
char Trackbarname3[50]; //用于存储滑动条3的名称
char Trackbarname4[50]; //用于存储滑动条4的名称

int pre_place1, pre_place2; //滑动条对应的变量,两个阈值变量
int pre_place3, pre_place4; //滑动条对应的变量,两个阈值变量
const int max_place = 255; //定义Trackbar的最大值
const int hough_max_place = 200;

int main()
{
Mat src = imread("car_gray.png");
Mat img = imread("lena_gray.png");
Mat gaosi_src, gaosi_img;
cv::GaussianBlur(src, gaosi_src, cv::Size(5, 5), 3, 3);
cv::GaussianBlur(img, gaosi_img, cv::Size(5, 5), 3, 3);

namedWindow("gaosi_src", WINDOW_AUTOSIZE);
namedWindow("gaosi_img", WINDOW_AUTOSIZE);
imshow("gaosi_src", gaosi_src);
imshow("gaosi_img", gaosi_img);
imwrite("gaosi_src.png", gaosi_src);
imwrite("gaosi_img.png", gaosi_img);

Mat canny_src, canny_img;

/*
namedWindow(WindowNameSrc, WINDOW_AUTOSIZE); //定义自动适应图片大小的窗口
namedWindow(WindowNameImg, WINDOW_AUTOSIZE); //定义自动适应图片大小的窗口

sprintf(Trackbarname1, "阈值1 %d", max_place);//sprintf函数,格式化赋值
sprintf(Trackbarname2, "阈值2 %d", max_place);//sprintf函数,格式化赋值
sprintf(Trackbarname3, "阈值3 %d", max_place);//sprintf函数,格式化赋值
sprintf(Trackbarname4, "阈值4 %d", max_place);//sprintf函数,格式化赋值

createTrackbar(Trackbarname1, WindowNameSrc, &pre_place1, max_place); //创建滑动条
createTrackbar(Trackbarname2, WindowNameSrc, &pre_place2, max_place); //创建滑动条
createTrackbar(Trackbarname3, WindowNameImg, &pre_place3, max_place); //创建滑动条
createTrackbar(Trackbarname4, WindowNameImg, &pre_place4, max_place); //创建滑动条

while (1) {
pre_place1 = getTrackbarPos(Trackbarname1, WindowNameSrc); //获取滑动条当前位置
pre_place2 = getTrackbarPos(Trackbarname2, WindowNameSrc); //获取滑动条当前位置
pre_place3 = getTrackbarPos(Trackbarname3, WindowNameImg); //获取滑动条当前位置
pre_place4 = getTrackbarPos(Trackbarname4, WindowNameImg); //获取滑动条当前位置

Canny(gaosi_src, canny_src, pre_place1, pre_place2, 3);
Canny(gaosi_img, canny_img, pre_place3, pre_place4, 3);

imshow(WindowNameSrc, canny_src);
imshow(WindowNameImg, canny_img);

if (waitKey(10) == 27) break; //按下Esc键退出程序
}
*/

Canny(gaosi_src, canny_src, 246, 0, 3);
Canny(gaosi_img, canny_img, 102, 12, 3);

imshow(WindowNameSrc, canny_src);
imshow(WindowNameImg, canny_img);
imwrite("canny_src.png", canny_src);
imwrite("canny_img.png", canny_img);

//进行霍夫线变换
vector<Vec2f> lines_src;//用于储存参数空间的交点
vector<Vec2f> lines_img;//用于储存参数空间的交点
Mat src_result = src.clone();
Mat img_result = img.clone();
imshow("src_result", src_result);
imshow("img_result", img_result);
/*
HoughLines(canny_src, lines, 1, CV_PI / 180, 120, 0, 0);//针对不同像素的图片注意调整阈值
const int alpha = 1000;//alpha取得充分大,保证画出贯穿整个图片的直线
//lines中存储的是边缘直线在极坐标空间下的rho和theta值,在图像空间(直角坐标系下)只能体现出一个点
//以该点为基准,利用theta与斜率之间的关系,找出该直线上的其他两个点(可能不在图像上),之后以这两点画出直线
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], theta = lines[i][1];
double cs = cos(theta), sn = sin(theta);
double x = rho * cs, y = rho * sn;
Point pt1(cvRound(x + alpha * (-sn)), cvRound(y + alpha * cs));
Point pt2(cvRound(x - alpha * (-sn)), cvRound(y - alpha * cs));
line(mResult, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}

namedWindow("hough_src", WINDOW_NORMAL);
imshow("hough_src", mResult);
*/

namedWindow(WindowNameHoughSrc, WINDOW_AUTOSIZE); //定义自动适应图片大小的窗口
namedWindow(WindowNameHoughImg, WINDOW_AUTOSIZE); //定义自动适应图片大小的窗口

sprintf(Trackbarname1, "阈值1 %d", hough_max_place);//sprintf函数,格式化赋值
sprintf(Trackbarname2, "阈值2 %d", hough_max_place);//sprintf函数,格式化赋值

createTrackbar(Trackbarname1, WindowNameHoughSrc, &pre_place1, hough_max_place); //创建滑动条
createTrackbar(Trackbarname2, WindowNameHoughImg, &pre_place2, hough_max_place); //创建滑动条

while (1) {
pre_place1 = getTrackbarPos(Trackbarname1, WindowNameHoughSrc); //获取滑动条当前位置
pre_place2 = getTrackbarPos(Trackbarname2, WindowNameHoughImg); //获取滑动条当前位置

HoughLines(canny_src, lines_src, 1, CV_PI / 180, 100, 0, 0);//针对不同像素的图片注意调整阈值
HoughLines(canny_img, lines_img, 1, CV_PI / 180, 100, 0, 0);//针对不同像素的图片注意调整阈值
const int alpha = 1000;
for (size_t i = 0; i < lines_src.size(); i++)
{
float rho = lines_src[i][0], theta = lines_src[i][1];
double cs = cos(theta), sn = sin(theta);
double x = rho * cs, y = rho * sn;
Point pt1(cvRound(x + alpha * (-sn)), cvRound(y + alpha * cs));
Point pt2(cvRound(x - alpha * (-sn)), cvRound(y - alpha * cs));
line(src_result, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
for (size_t i = 0; i < lines_img.size(); i++)
{
float rho = lines_img[i][0], theta = lines_img[i][1];
double cs = cos(theta), sn = sin(theta);
double x = rho * cs, y = rho * sn;
Point pt1(cvRound(x + alpha * (-sn)), cvRound(y + alpha * cs));
Point pt2(cvRound(x - alpha * (-sn)), cvRound(y - alpha * cs));
line(img_result, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
imshow(WindowNameHoughSrc, src_result);
imshow(WindowNameHoughImg, img_result);
imwrite("src_result.png", src_result);
imwrite("img_result.png", img_result);

if (waitKey(10) == 27) break; //按下Esc键退出程序
}
//依次在图中绘制出每条线段


waitKey(0);
return 0;
}

本文使用 CC BY-NC-SA 3.0 中国大陆 协议许可
具体请参见 知识共享协议

本文链接:https://zyhang8.github.io/2019/11/21/cv-exp4/