Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/cpp/fitellipse.cpp
16337 views
1
/********************************************************************************
2
*
3
*
4
* This program is demonstration for ellipse fitting. Program finds
5
* contours and approximate it by ellipses using three methods.
6
* 1: OpenCV's original method fitEllipse which implements Fitzgibbon 1995 method.
7
* 2: The Approximate Mean Square (AMS) method fitEllipseAMS proposed by Taubin 1991
8
* 3: The Direct least square (Direct) method fitEllipseDirect proposed by Fitzgibbon1999.
9
*
10
* Trackbar specify threshold parameter.
11
*
12
* White lines is contours/input points and the true ellipse used to generate the data.
13
* 1: Blue lines is fitting ellipses using openCV's original method.
14
* 2: Green lines is fitting ellipses using the AMS method.
15
* 3: Red lines is fitting ellipses using the Direct method.
16
*
17
*
18
* Original Author: Denis Burenkov
19
* AMS and Direct Methods Author: Jasper Shemilt
20
*
21
*
22
********************************************************************************/
23
#include "opencv2/imgproc.hpp"
24
#include "opencv2/imgcodecs.hpp"
25
#include "opencv2/highgui.hpp"
26
#include <iostream>
27
28
using namespace cv;
29
using namespace std;
30
31
class canvas{
32
public:
33
bool setupQ;
34
cv::Point origin;
35
cv::Point corner;
36
int minDims,maxDims;
37
double scale;
38
int rows, cols;
39
cv::Mat img;
40
41
void init(int minD, int maxD){
42
// Initialise the canvas with minimum and maximum rows and column sizes.
43
minDims = minD; maxDims = maxD;
44
origin = cv::Point(0,0);
45
corner = cv::Point(0,0);
46
scale = 1.0;
47
rows = 0;
48
cols = 0;
49
setupQ = false;
50
}
51
52
void stretch(cv::Point2f min, cv::Point2f max){
53
// Stretch the canvas to include the points min and max.
54
if(setupQ){
55
if(corner.x < max.x){corner.x = (int)(max.x + 1.0);};
56
if(corner.y < max.y){corner.y = (int)(max.y + 1.0);};
57
if(origin.x > min.x){origin.x = (int) min.x;};
58
if(origin.y > min.y){origin.y = (int) min.y;};
59
} else {
60
origin = cv::Point((int)min.x, (int)min.y);
61
corner = cv::Point((int)(max.x + 1.0), (int)(max.y + 1.0));
62
}
63
64
int c = (int)(scale*((corner.x + 1.0) - origin.x));
65
if(c<minDims){
66
scale = scale * (double)minDims/(double)c;
67
} else {
68
if(c>maxDims){
69
scale = scale * (double)maxDims/(double)c;
70
}
71
}
72
int r = (int)(scale*((corner.y + 1.0) - origin.y));
73
if(r<minDims){
74
scale = scale * (double)minDims/(double)r;
75
} else {
76
if(r>maxDims){
77
scale = scale * (double)maxDims/(double)r;
78
}
79
}
80
cols = (int)(scale*((corner.x + 1.0) - origin.x));
81
rows = (int)(scale*((corner.y + 1.0) - origin.y));
82
setupQ = true;
83
}
84
85
void stretch(vector<Point2f> pts)
86
{ // Stretch the canvas so all the points pts are on the canvas.
87
cv::Point2f min = pts[0];
88
cv::Point2f max = pts[0];
89
for(size_t i=1; i < pts.size(); i++){
90
Point2f pnt = pts[i];
91
if(max.x < pnt.x){max.x = pnt.x;};
92
if(max.y < pnt.y){max.y = pnt.y;};
93
if(min.x > pnt.x){min.x = pnt.x;};
94
if(min.y > pnt.y){min.y = pnt.y;};
95
};
96
stretch(min, max);
97
}
98
99
void stretch(cv::RotatedRect box)
100
{ // Stretch the canvas so that the rectangle box is on the canvas.
101
cv::Point2f min = box.center;
102
cv::Point2f max = box.center;
103
cv::Point2f vtx[4];
104
box.points(vtx);
105
for( int i = 0; i < 4; i++ ){
106
cv::Point2f pnt = vtx[i];
107
if(max.x < pnt.x){max.x = pnt.x;};
108
if(max.y < pnt.y){max.y = pnt.y;};
109
if(min.x > pnt.x){min.x = pnt.x;};
110
if(min.y > pnt.y){min.y = pnt.y;};
111
}
112
stretch(min, max);
113
}
114
115
void drawEllipseWithBox(cv::RotatedRect box, cv::Scalar color, int lineThickness)
116
{
117
if(img.empty()){
118
stretch(box);
119
img = cv::Mat::zeros(rows,cols,CV_8UC3);
120
}
121
122
box.center = scale * cv::Point2f(box.center.x - origin.x, box.center.y - origin.y);
123
box.size.width = (float)(scale * box.size.width);
124
box.size.height = (float)(scale * box.size.height);
125
126
ellipse(img, box, color, lineThickness, LINE_AA);
127
128
Point2f vtx[4];
129
box.points(vtx);
130
for( int j = 0; j < 4; j++ ){
131
line(img, vtx[j], vtx[(j+1)%4], color, lineThickness, LINE_AA);
132
}
133
}
134
135
void drawPoints(vector<Point2f> pts, cv::Scalar color)
136
{
137
if(img.empty()){
138
stretch(pts);
139
img = cv::Mat::zeros(rows,cols,CV_8UC3);
140
}
141
for(size_t i=0; i < pts.size(); i++){
142
Point2f pnt = scale * cv::Point2f(pts[i].x - origin.x, pts[i].y - origin.y);
143
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[0] = (uchar)color[0];
144
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[1] = (uchar)color[1];
145
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[2] = (uchar)color[2];
146
};
147
}
148
149
void drawLabels( std::vector<std::string> text, std::vector<cv::Scalar> colors)
150
{
151
if(img.empty()){
152
img = cv::Mat::zeros(rows,cols,CV_8UC3);
153
}
154
int vPos = 0;
155
for (size_t i=0; i < text.size(); i++) {
156
cv::Scalar color = colors[i];
157
std::string txt = text[i];
158
Size textsize = getTextSize(txt, FONT_HERSHEY_COMPLEX, 1, 1, 0);
159
vPos += (int)(1.3 * textsize.height);
160
Point org((img.cols - textsize.width), vPos);
161
cv::putText(img, txt, org, FONT_HERSHEY_COMPLEX, 1, color, 1, LINE_8);
162
}
163
}
164
165
};
166
167
static void help()
168
{
169
cout <<
170
"\nThis program is demonstration for ellipse fitting. The program finds\n"
171
"contours and approximate it by ellipses. Three methods are used to find the \n"
172
"elliptical fits: fitEllipse, fitEllipseAMS and fitEllipseDirect.\n"
173
"Call:\n"
174
"./fitellipse [image_name -- Default ../data/stuff.jpg]\n" << endl;
175
}
176
177
int sliderPos = 70;
178
179
Mat image;
180
181
bool fitEllipseQ, fitEllipseAMSQ, fitEllipseDirectQ;
182
cv::Scalar fitEllipseColor = Scalar(255, 0, 0);
183
cv::Scalar fitEllipseAMSColor = Scalar( 0,255, 0);
184
cv::Scalar fitEllipseDirectColor = Scalar( 0, 0,255);
185
cv::Scalar fitEllipseTrueColor = Scalar(255,255,255);
186
187
void processImage(int, void*);
188
189
int main( int argc, char** argv )
190
{
191
fitEllipseQ = true;
192
fitEllipseAMSQ = true;
193
fitEllipseDirectQ = true;
194
195
cv::CommandLineParser parser(argc, argv,"{help h||}{@image|../data/ellipses.jpg|}");
196
if (parser.has("help"))
197
{
198
help();
199
return 0;
200
}
201
string filename = parser.get<string>("@image");
202
image = imread(filename, 0);
203
if( image.empty() )
204
{
205
cout << "Couldn't open image " << filename << "\n";
206
return 0;
207
}
208
209
imshow("source", image);
210
namedWindow("result", WINDOW_NORMAL );
211
212
// Create toolbars. HighGUI use.
213
createTrackbar( "threshold", "result", &sliderPos, 255, processImage );
214
215
processImage(0, 0);
216
217
// Wait for a key stroke; the same function arranges events processing
218
waitKey();
219
return 0;
220
}
221
222
// Define trackbar callback function. This function finds contours,
223
// draws them, and approximates by ellipses.
224
void processImage(int /*h*/, void*)
225
{
226
RotatedRect box, boxAMS, boxDirect;
227
vector<vector<Point> > contours;
228
Mat bimage = image >= sliderPos;
229
230
findContours(bimage, contours, RETR_LIST, CHAIN_APPROX_NONE);
231
232
canvas paper;
233
paper.init(int(0.8*MIN(bimage.rows, bimage.cols)), int(1.2*MAX(bimage.rows, bimage.cols)));
234
paper.stretch(cv::Point2f(0.0f, 0.0f), cv::Point2f((float)(bimage.cols+2.0), (float)(bimage.rows+2.0)));
235
236
std::vector<std::string> text;
237
std::vector<cv::Scalar> color;
238
239
if (fitEllipseQ) {
240
text.push_back("OpenCV");
241
color.push_back(fitEllipseColor);
242
}
243
if (fitEllipseAMSQ) {
244
text.push_back("AMS");
245
color.push_back(fitEllipseAMSColor);
246
}
247
if (fitEllipseDirectQ) {
248
text.push_back("Direct");
249
color.push_back(fitEllipseDirectColor);
250
}
251
paper.drawLabels(text, color);
252
253
int margin = 2;
254
vector< vector<Point2f> > points;
255
for(size_t i = 0; i < contours.size(); i++)
256
{
257
size_t count = contours[i].size();
258
if( count < 6 )
259
continue;
260
261
Mat pointsf;
262
Mat(contours[i]).convertTo(pointsf, CV_32F);
263
264
vector<Point2f>pts;
265
for (int j = 0; j < pointsf.rows; j++) {
266
Point2f pnt = Point2f(pointsf.at<float>(j,0), pointsf.at<float>(j,1));
267
if ((pnt.x > margin && pnt.y > margin && pnt.x < bimage.cols-margin && pnt.y < bimage.rows-margin)) {
268
if(j%20==0){
269
pts.push_back(pnt);
270
}
271
}
272
}
273
points.push_back(pts);
274
}
275
276
for(size_t i = 0; i < points.size(); i++)
277
{
278
vector<Point2f> pts = points[i];
279
280
if (pts.size()<=5) {
281
continue;
282
}
283
if (fitEllipseQ) {
284
box = fitEllipse(pts);
285
if( MAX(box.size.width, box.size.height) > MIN(box.size.width, box.size.height)*30 ||
286
MAX(box.size.width, box.size.height) <= 0 ||
287
MIN(box.size.width, box.size.height) <= 0){continue;};
288
}
289
if (fitEllipseAMSQ) {
290
boxAMS = fitEllipseAMS(pts);
291
if( MAX(boxAMS.size.width, boxAMS.size.height) > MIN(boxAMS.size.width, boxAMS.size.height)*30 ||
292
MAX(box.size.width, box.size.height) <= 0 ||
293
MIN(box.size.width, box.size.height) <= 0){continue;};
294
}
295
if (fitEllipseDirectQ) {
296
boxDirect = fitEllipseDirect(pts);
297
if( MAX(boxDirect.size.width, boxDirect.size.height) > MIN(boxDirect.size.width, boxDirect.size.height)*30 ||
298
MAX(box.size.width, box.size.height) <= 0 ||
299
MIN(box.size.width, box.size.height) <= 0 ){continue;};
300
}
301
302
if (fitEllipseQ) {
303
paper.drawEllipseWithBox(box, fitEllipseColor, 3);
304
}
305
if (fitEllipseAMSQ) {
306
paper.drawEllipseWithBox(boxAMS, fitEllipseAMSColor, 2);
307
}
308
if (fitEllipseDirectQ) {
309
paper.drawEllipseWithBox(boxDirect, fitEllipseDirectColor, 1);
310
}
311
312
paper.drawPoints(pts, cv::Scalar(255,255,255));
313
}
314
315
imshow("result", paper.img);
316
}
317
318