使用OpenCV识别直线和圆

过程基本分四步或者三步走。第一步,加载图片。第二步,灰度化图片。第三步,Canny边缘化,第四步,检测直线或者圆。
首先,说明下OpenCv表示图片的结构是IplImage。这个东西代表了很多有用的东西。接下来的几步,都有该结构的指针对应图片。
第一步,加载图片。可以直接加载成灰度图,或者加载成彩色图,或者加载为原有图片的格式,第二步再灰度化。
直接加载为灰度图,
IplImage* imgZero = cvLoadImage(szZero, CV_LOAD_IMAGE_GRAYSCALE);
加载为彩色图,则把cvLoadImage函数的第二个参数改为CV_LOAD_IMAGE_COLOR,改为CV_LOAD_IMAGE_ANYCOLOR则保持图片原有格式。
第二步,如果第一步不是按灰度图加载,那么在这一步需要灰度化。代码如下,
IplImage *imgGray = NULL;imgGray = cvCreateImage(cvGetSize(imgZero),IPL_DEPTH_8U,1);cvCvtColor(imgZero, imgGray ,CV_BGR2GRAY);

这几句代码的意思是根据原图片大小创建单通道位深为8的图片,然后把原有图片转换为创建的单通道8位深灰度图片。
第三步,canny处理。代码如下,
IplImage *imgCanny = cvCreateImage(cvGetSize(imgZero ),IPL_DEPTH_8U,1);cvCanny(imgGray , imgCanny, 50, 100);
这几句代码的意思是根据原图片大小创建单通道位深为8的图片,然后对灰度图片进行canny处理,处理结果存储在新建立的imgCanny图片中。
第四步,使用识别函数进行识别直线或者圆。
识别直线的函数是cvHoughLines2。识别圆的函数是cvHoughCircles。进行识别之前,先创建存储区。
CvMemStorage *storage = cvCreateMemStorage(0);

识别直线的代码如下,

1
2
3
4
5
6
7
8
9
10
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgZero, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[1].fX = line[0].x;
pts[1].fY = line[0].y;

识别圆的代码如下,
1
2
3
4
5
6
7
8
9
10
11
12
cvClearMemStorage(storage);
CvSeq * cir=NULL;
cir = cvHoughCircles(imgCanny, storage, CV_HOUGH_GRADIENT, 1, imgRecog->width/10 ,80,40, 50);
float * p=(float *)cvGetSeqElem(cir, 0);
CvPoint pt = cvPoint(cvRound(p[0]),cvRound(p[1]));
cvCircle(imgRecog,pt,cvRound(p[2]),CV_RGB(0,255,0));
cvCircle(imgRecog, pt, 5, CV_RGB(0,255,0), -1, 8, 0 );//绘制圆心
cout<<"圆心:"<<pt.x<<","<<pt.y<<endl;
cout<<"半径:"<<p[2]<<endl;
pts[0].fX = pt.x;
pts[0].fY = pt.y;
fR = p[2];

下面再提供一个识别圆和直线的程序的完整代码。识别直线和圆的参数设置,需要自己调节。
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
154
155
156
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>
#include <iostream>
#include <fstream>
using namespace std;

#pragma comment(lib, "opencv_core220.lib")
#pragma comment(lib, "opencv_highgui220.lib")
#pragma comment(lib, "opencv_imgproc220.lib")

struct Point
{
double fX;
double fY;
};

Point pts[4];
double fR;

int main()
{
//打开输出文件
ofstream outf("out.txt");
//获取cout默认输出
streambuf *default_buf=cout.rdbuf();
//重定向cout输出到文件
cout.rdbuf( outf.rdbuf() );

char* szZero = "仪表盘0.bmp";
char* szOne = "仪表盘1.bmp";
char* szRecog = "仪表盘.bmp";
IplImage* imgZero = cvLoadImage(szZero, 1);
if (!imgZero) return -1;
IplImage* imgOne = cvLoadImage(szOne, 1);
if (!imgOne) return -1;
IplImage* imgRecog = cvLoadImage(szRecog, 1);
if (!imgRecog) return -1;

IplImage *imgEdge = NULL;
imgEdge = cvCreateImage(cvGetSize(imgRecog),IPL_DEPTH_8U,1);
IplImage *imgCanny = cvCreateImage(cvGetSize(imgRecog),IPL_DEPTH_8U,1);

CvMemStorage *storage = cvCreateMemStorage(0);//内存采用默认大小

cvCvtColor(imgZero, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化检测刻度0直线
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgZero, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[1].fX = line[0].x;
pts[1].fY = line[0].y;

cvCvtColor(imgOne, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化检测刻度1直线
cvClearMemStorage(storage);
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgOne, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[2].fX = line[0].x;
pts[2].fY = line[0].y;

cvCvtColor(imgRecog, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);
//hough变化圆检测
cvClearMemStorage(storage);
CvSeq * cir=NULL;
cir = cvHoughCircles(imgCanny, storage, CV_HOUGH_GRADIENT, 1, imgRecog->width/10 ,80,40, 50);
float * p=(float *)cvGetSeqElem(cir, 0);
CvPoint pt = cvPoint(cvRound(p[0]),cvRound(p[1]));
cvCircle(imgRecog,pt,cvRound(p[2]),CV_RGB(0,255,0));
cvCircle(imgRecog, pt, 5, CV_RGB(0,255,0), -1, 8, 0 );//绘制圆心
cout<<"圆心:"<<pt.x<<","<<pt.y<<endl;
cout<<"半径:"<<p[2]<<endl;
pts[0].fX = pt.x;
pts[0].fY = pt.y;
fR = p[2];

//hough变化检测指针直线
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
line = (CvPoint*)cvGetSeqElem(lines, 0);
cvLine(imgRecog, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cout<<"端点1:"<< line[0].x << "," << line[0].y<<endl;
cout<<"端点2:"<< line[1].y << "," << line[1].y<<endl;
pts[3].fX = line[0].x;
pts[3].fY = line[0].y;

static const double PI = atan(1.0) * 4;
double fKZero = (pts[1].fY - pts[0].fY) / (pts[1].fX - pts[0].fX);
double fThetaZero = 180.0 / PI * atan(fKZero);
if (pts[1].fX + 10 < pts[0].fX)//左半部分
{
fThetaZero += 180.0;
}
fThetaZero += 360.0;
if (fThetaZero > 360.0)
{
fThetaZero -= 360.0;
}

double fKOne = (pts[2].fY - pts[0].fY) / (pts[2].fX - pts[0].fX);
double fThetaOne = 180.0 / PI * atan(fKOne);
if (pts[2].fX + 10 < pts[0].fX)//左半部分
{
fThetaOne += 180.0;
}
fThetaOne += 360.0;
if (fThetaOne > 360.0)
{
fThetaOne -= 360.0;
}

double fKRecog = (pts[3].fY - pts[0].fY) / (pts[3].fX - pts[0].fX);
double fThetaRecog = 180.0 / PI * atan(fKRecog);
if (pts[3].fX + 10 < pts[0].fX)//左半部分
{
fThetaRecog += 180.0;
}
fThetaRecog += 360.0;
if (fThetaRecog > 360.0)
{
fThetaRecog -= 360.0;
}

char szAns[100];
sprintf(szAns, "0刻度的角度为:%f,1刻度的角度为:%f,识别刻度的角度为:%f,识别结果为:%.2f",
fThetaZero, fThetaOne, fThetaRecog, (fThetaRecog - fThetaZero) / (fThetaOne - fThetaZero));
cout << szAns << endl;

cvNamedWindow("检测", CV_WINDOW_AUTOSIZE);
cvShowImage("检测", imgRecog);
cvNamedWindow("刻度0", CV_WINDOW_AUTOSIZE);
cvShowImage("刻度0", imgZero);
cvNamedWindow("刻度1", CV_WINDOW_AUTOSIZE);
cvShowImage("刻度1", imgOne);
cvWaitKey(0);

cvDestroyWindow("检测");
cvDestroyWindow("刻度0");
cvDestroyWindow("刻度1");
cvReleaseImage(&imgRecog);
cvReleaseImage(&imgZero);
cvReleaseImage(&imgOne);
cvReleaseImage(&imgEdge);
cvReleaseImage(&imgCanny);
cvReleaseMemStorage(&storage);
}

再给出一个识别图片内所有直线的代码,当然不可能有这么好的效果,
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
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>

#pragma comment(lib, "opencv_core220.lib")
#pragma comment(lib, "opencv_highgui220.lib")
#pragma comment(lib, "opencv_imgproc220.lib")

int main()
{
char* szPic = "pic_6.jpg";
IplImage* imgOrigin = cvLoadImage(szPic, 1);
if (!imgOrigin) return -1;

IplImage *imgEdge = NULL;
imgEdge = cvCreateImage(cvGetSize(imgOrigin),IPL_DEPTH_8U,1);
IplImage *imgCanny = cvCreateImage(cvGetSize(imgOrigin),IPL_DEPTH_8U,1);

CvMemStorage *storage = cvCreateMemStorage(0);//内存采用默认大小

cvCvtColor(imgOrigin, imgEdge,CV_BGR2GRAY);
cvCanny(imgEdge, imgCanny, 50, 100);

//hough变化检测刻度0直线
cvClearMemStorage(storage);
CvPoint* line;
CvSeq* lines;
lines = cvHoughLines2(imgCanny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 10, 10 );
for (int i = 0; i < lines->total; ++i)
{
line = (CvPoint*)cvGetSeqElem(lines, i);
cvLine(imgOrigin, line[0], line[1], CV_RGB(255,0,0), 1, CV_AA, 0 );
}

cvNamedWindow("检测", CV_WINDOW_AUTOSIZE);
cvShowImage("检测", imgOrigin);
cvWaitKey(0);
cvDestroyWindow("检测");
cvReleaseImage(&imgOrigin);
cvReleaseImage(&imgEdge);
cvReleaseImage(&imgCanny);
cvReleaseMemStorage(&storage);
}