Main.cpp
#include <cv.h>
#include <highgui.h>
#include "EllipseDetectorYaed.h"
#include <fstream>
using namespace std;
using namespace cv;
定义了如下所示一些列函数
保存椭圆
void SaveEllipses(const string& workingDir, const string& imgName, const vector<Ellipse>& ellipses /*, const vector<double>& times*/)
{
string path(workingDir + "/" + imgName + ".txt");
ofstream out(path, ofstream::out | ofstream::trunc);
if (!out.good())
{
cout << "Error saving: " << path << endl;
return;
}
// Save execution time
//out << times[0] << "\t" << times[1] << "\t" << times[2] << "\t" << times[3] << "\t" << times[4] << "\t" << times[5] << "\t" << "\n";
unsigned n = ellipses.size();
// Save number of ellipses
out << n << "\n";
// Save ellipses
for (unsigned i = 0; i < n; ++i)
{
const Ellipse& e = ellipses[i];
out << e._xc << "\t" << e._yc << "\t" << e._a << "\t" << e._b << "\t" << e._rad << "\t" << e._score << "\n";
}
out.close();
}
输入为两个字符串和一个代表椭圆的vector,其中,字符串分别为目录和存储椭圆图片的名称;
函数目的:
1.将椭圆图片命名并保存在制定路径
2.输出椭圆个数,输出椭圆的中心及a,b(e._rad与e._score暂时未知)
加载测试的(没太看懂)
// Should be checked
bool LoadTest(vector<Ellipse>& ellipses, const string& sTestFileName, vector<double>& times, bool bIsAngleInRadians = true)
{
ifstream in(sTestFileName);
//ifstream in("c:\\pdos.def");//以输入方式打开文件
if (!in.good())
{
cout << "Error opening: " << sTestFileName << endl;
return false;
}
//如果打开失败,报错
times.resize(6);
in >> times[0] >> times[1] >> times[2] >> times[3] >> times[4] >> times[5];
unsigned n;
in >> n;
ellipses.clear();
if (n == 0) return true;
ellipses.reserve(n);
while (in.good() && n--)
{
Ellipse e;
in >> e._xc >> e._yc >> e._a >> e._b >> e._rad >> e._score;
//默认进不去这个if
if (!bIsAngleInRadians)
{
e._rad = e._rad * float(CV_PI / 180.0);
}
e._rad = fmod(float(e._rad + 2.0*CV_PI), float(CV_PI));
if ((e._a > 0) && (e._b > 0) && (e._rad >= 0))
{
ellipses.push_back(e);
}
//有测试图片时,把椭圆里ab,rad为正值的pushback入ellipse
}
in.close();
// Sort ellipses by decreasing score
sort(ellipses.begin(), ellipses.end());
return true;
}
加载GT(好的测试)
void LoadGT(vector<Ellipse>& gt, const string& sGtFileName, bool bIsAngleInRadians = true)
{
ifstream in(sGtFileName);
if (!in.good())
{
cout << "Error opening: " << sGtFileName << endl;
return;
}
//检查报错
unsigned n;
in >> n;
gt.clear();
gt.reserve(n);
while (in.good() && n--)
{
Ellipse e;
in >> e._xc >> e._yc >> e._a >> e._b >> e._rad;
if (!bIsAngleInRadians)
{
// convert to radians
e._rad = float(e._rad * CV_PI / 180.0);
}
if (e._a < e._b)
{
float temp = e._a;
e._a = e._b;
e._b = temp;
e._rad = e._rad + float(0.5*CV_PI);
}
e._rad = fmod(float(e._rad + 2.f*CV_PI), float(CV_PI));
e._score = 1.f;
gt.push_back(e);
}
in.close();
}
和上段的代码相似
测试重叠部分
bool TestOverlap(const Mat1b& gt, const Mat1b& test, float th)
{
float fAND = float(countNonZero(gt & test));
//对二值化图像执行countNonZero,可得到非零像素点数
float fOR = float(countNonZero(gt | test));
float fsim = fAND / fOR;
return (fsim >= th);
}
输入的Mat1b是unchar型的mat,输入了两个mat和一个float(阈值)
函数功能:
两图重叠部分大于一定阈值时输出为true
int Count(const vector<bool> v)
{
int counter = 0;
for (unsigned i = 0; i < v.size(); ++i)
{
if (v[i]) { ++counter; }
}
return counter;
}
函数功能:
输出输入的vector<bool>中true的个数
// Should be checked !!!!!
std::tuple<float, float, float> Evaluate(const vector<Ellipse>& ellGT, const vector<Ellipse>& ellTest, const float th_score, const Mat3b& img)
{
float threshold_overlap = 0.8f;
//float threshold = 0.95f;
//给交叠区域一个阈值
unsigned sz_gt = ellGT.size();
unsigned size_test = ellTest.size();
unsigned sz_test = unsigned(min(1000, int(size_test)));
vector<Mat1b> gts(sz_gt);
vector<Mat1b> tests(sz_test);
for (unsigned i = 0; i < sz_gt; ++i)
{
const Ellipse& e = ellGT[i];
Mat1b tmp(img.rows, img.cols, uchar(0));
ellipse(tmp, Point(e._xc, e._yc), Size(e._a, e._b), e._rad * 180.0 / CV_PI, 0.0, 360.0, Scalar(255), -1);
//绘制椭圆,ellipse是opencv函数
gts[i] = tmp;
}
for (unsigned i = 0; i < sz_test; ++i)
{
const Ellipse& e = ellTest[i];
Mat1b tmp(img.rows, img.cols, uchar(0));
ellipse(tmp, Point(e._xc, e._yc), Size(e._a, e._b), e._rad * 180.0 / CV_PI, 0.0, 360.0, Scalar(255), -1);
tests[i] = tmp;
//同理绘制椭圆
}
Mat1b overlap(sz_gt, sz_test, uchar(0));
for (int r = 0; r < overlap.rows; ++r)
{
for (int c = 0; c < overlap.cols; ++c)
{
overlap(r, c) = TestOverlap(gts[r], tests[c], threshold_overlap) ? uchar(255) : uchar(0);
}
}
int counter = 0;
vector<bool> vec_gt(sz_gt, false);
for (int i = 0; i < sz_test; ++i)
{
const Ellipse& e = ellTest[i];
for (int j = 0; j < sz_gt; ++j)
{
if (vec_gt[j]) { continue; }
bool bTest = overlap(j, i) != 0;
//若overlap非0则为真,也即交叠区域大于阈值为真
if (bTest)
{
vec_gt[j] = true;
break;
}
}
}
int tp = Count(vec_gt);//计算了交叠区域里为true的个数
int fn = int(sz_gt) - tp;//不交叠的个数
int fp = size_test - tp; // 不交叠的个数
float pr(0.f);
float re(0.f);
float fmeasure(0.f);
if (tp == 0)
{
if (fp == 0)
{
pr = 1.f;
re = 0.f;
fmeasure = (2.f * pr * re) / (pr + re);
}
else
{
pr = 0.f;
re = 0.f;
fmeasure = 0.f;
}
}
else
{
pr = float(tp) / float(tp + fp);
re = float(tp) / float(tp + fn);
fmeasure = (2.f * pr * re) / (pr + re);
}
return make_tuple(pr, re, fmeasure);
//返回交叠个数分别占两组椭圆的总个数的比例
}
std::tuple是类似pair的模板。每个pair的成员类型都不相同,但每个pair都恰好有两个成员。不同std::tuple类型的成员类型也不相同,但一个std::tuple可以有任意数量的成员。每个确定的std::tuple类型的成员数目是固定的,但一个std::tuple类型的成员数目可以与另一个std::tuple类型不同。
参考网页:https://blog.csdn.net/fengbingchun/article/details/72835446
函数功能
返回交叠个数分别占两组椭圆的总个数的比例(似乎用于评测)
单张图片测试主函数
// Test on single image
int main2()
{
string images_folder = "你的路径";
string out_folder = "你的路径";
//路径
vector<string> names;
glob(images_folder + "Lo3my4.*", names);
//glob() 函数返回匹配指定模式的文件名或目录。
for (const auto& image_name : names)
{
string name = image_name.substr(image_name.find_last_of("\\") + 1);
name = name.substr(0, name.find_last_of("."));
Mat3b image = imread(image_name);
Size sz = image.size();
// Convert to grayscale
Mat1b gray;
cvtColor(image, gray, CV_BGR2GRAY);
// Parameters Settings (Sect. 4.2)
//设置一系列参数
int iThLength = 16;//小于该阈值的圆弧舍弃
float fThObb = 3.0f;//小圆弧舍弃
float fThPos = 1.0f;
float fTaoCenters = 0.05f;//舍弃中心太远的圆弧对
int iNs = 16;
float fMaxCenterDistance = sqrt(float(sz.width*sz.width + sz.height*sz.height)) * fTaoCenters;
float fThScoreScore = 0.7f;
// Other constant parameters settings.
// Gaussian filter parameters, in pre-processing
Size szPreProcessingGaussKernelSize = Size(5, 5);
double dPreProcessingGaussSigma = 1.0;
float fDistanceToEllipseContour = 0.1f; // (Sect. 3.3.1 - Validation)
float fMinReliability = 0.5; // Const parameters to discard bad ellipses
CEllipseDetectorYaed* yaed = new CEllipseDetectorYaed();
yaed->SetParameters(szPreProcessingGaussKernelSize,
dPreProcessingGaussSigma,
fThPos,
fMaxCenterDistance,
iThLength,
fThObb,
fDistanceToEllipseContour,
fThScoreScore,
fMinReliability,
iNs
);
// Detect
vector<Ellipse> ellsYaed;
Mat1b gray2 = gray.clone();
yaed->Detect(gray2, ellsYaed);
vector<double> times = yaed->GetTimes();
cout << "--------------------------------" << endl;
cout << "Execution Time: " << endl;
cout << "Edge Detection: \t" << times[0] << endl;
cout << "Pre processing: \t" << times[1] << endl;
cout << "Grouping: \t" << times[2] << endl;
cout << "Estimation: \t" << times[3] << endl;
cout << "Validation: \t" << times[4] << endl;
cout << "Clustering: \t" << times[5] << endl;
cout << "--------------------------------" << endl;
cout << "Total: \t" << yaed->GetExecTime() << endl;
cout << "--------------------------------" << endl;
Mat3b resultImage = image.clone();
yaed->DrawDetectedEllipses(resultImage, ellsYaed);
imwrite(out_folder + name + ".png", resultImage);
imshow("Yaed", resultImage);
waitKey();
int yghds = 0;
}
return 0;
}
设置参数,把参数放入类中,进入Detect函数,得到存放椭圆的<vector>ellsYaed
利用DrawDetectedEllipses函数绘画所有椭圆,将椭圆绘制后的图片存储。
Detect函数:
void CEllipseDetectorYaed::Detect(Mat1b& I, vector<Ellipse>& ellipses)
{
Tic(1); //prepare data structure
// Set the image size
_szImg = I.size();
// Initialize temporary data structures
Mat1b DP = Mat1b::zeros(_szImg); // arcs along positive diagonal
Mat1b DN = Mat1b::zeros(_szImg); // arcs along negative diagonal
// Initialize accumulator dimensions
ACC_N_SIZE = 101;//N=B/A
ACC_R_SIZE = 180;//R = rho = atan(K)
ACC_A_SIZE = max(_szImg.height, _szImg.width);//A,3个ACC_都为int
// Allocate accumulators
accN = new int[ACC_N_SIZE];
accR = new int[ACC_R_SIZE];
accA = new int[ACC_A_SIZE];
// Other temporary
VVP points_1, points_2, points_3, points_4; //vector of points, one for each convexity class,vector<vector<point>>
unordered_map<uint, EllipseData> centers; //hash map for reusing already computed EllipseData
Toc(1); //prepare data structure
// Preprocessing
// From input image I, find edge point with coarse convexity along positive (DP) or negative (DN) diagonal
PrePeocessing(I, DP, DN);//高斯处理后用canny3提取边缘,对每一个边缘求方向,相角大于0的点dp=255,小于0,dn=255
// Detect edges and find convexities
DetectEdges13(DP, points_1, points_3);//角度为+的一类,然后分类1,3类圆弧
DetectEdges24(DN, points_2, points_4);
Toc(1); //preprocessing
// DEBUG
Mat3b out(I.rows, I.cols, Vec3b(0,0,0));
for(unsigned i=0; i<points_1.size(); ++i)
{
//Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
Vec3b color(255,0,0);
for(unsigned j=0; j<points_1[i].size(); ++j)
out(points_1[i][j]) = color;
}
for(unsigned i=0; i<points_2.size(); ++i)
{
//Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
Vec3b color(0,255,0);
for(unsigned j=0; j<points_2[i].size(); ++j)
out(points_2[i][j]) = color;
}
for(unsigned i=0; i<points_3.size(); ++i)
{
//Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
Vec3b color(0,0,255);
for(unsigned j=0; j<points_3[i].size(); ++j)
out(points_3[i][j]) = color;
}
for(unsigned i=0; i<points_4.size(); ++i)
{
//Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
Vec3b color(255,0,255);
for(unsigned j=0; j<points_4[i].size(); ++j)
out(points_4[i][j]) = color;
}
// time estimation, validation inside
Tic(2); //grouping
//find triplets,论文中的三元组
Triplets124(points_1, points_2, points_4, centers, ellipses);//大致看了一下,通过三元组获得椭圆与椭圆中心,函数中有设置阈值进行圆弧的额筛选
Triplets231(points_2, points_3, points_1, centers, ellipses);
Triplets342(points_3, points_4, points_2, centers, ellipses);
Triplets413(points_4, points_1, points_3, centers, ellipses);
Toc(2); //grouping
// time estimation, validation inside
_times[2] -= (_times[3] + _times[4]);
Tac(4); //validation
// Sort detected ellipses with respect to score
sort(ellipses.begin(), ellipses.end());
Toc(4); //validation
// Free accumulator memory
delete[] accN;
delete[] accR;
delete[] accA;
Tic(5);
// Cluster detections
ClusterEllipses(ellipses);//聚类,存出相似椭圆中socre高的那个
Toc(5);
};
函数功能:输出提取到的椭圆
这是论文内容中比较重要的函数,引用了很多自定义函数,建议看一下源代码。
网友评论