3864 lines
114 KiB
C++
3864 lines
114 KiB
C++
#include "ellipse_detector.h"
|
|
#include <numeric>
|
|
#include <time.h>
|
|
#include <opencv2/imgproc/types_c.h>
|
|
|
|
using namespace std;
|
|
using namespace cv;
|
|
|
|
|
|
|
|
#ifndef USE_OMP
|
|
int omp_get_max_threads() { return 1; }
|
|
int omp_get_thread_num() { return 0; }
|
|
// int omp_set_num_threads(int){ return 0; }
|
|
#endif
|
|
|
|
|
|
namespace yaed {
|
|
|
|
void _list_dir(std::string dir, std::vector<std::string>& files, std::string suffixs, bool r) {
|
|
// assert(_endswith(dir, "/") || _endswith(dir, "\\"));
|
|
|
|
DIR *pdir;
|
|
struct dirent *ent;
|
|
string childpath;
|
|
string absolutepath;
|
|
pdir = opendir(dir.c_str());
|
|
assert(pdir != NULL);
|
|
|
|
vector<string> suffixd(0);
|
|
if (!suffixs.empty() && suffixs != "") {
|
|
suffixd = _split(suffixs, "|");
|
|
}
|
|
|
|
while ((ent = readdir(pdir)) != NULL) {
|
|
if (ent->d_type & DT_DIR) {
|
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
if (r) { // If need to traverse subdirectories
|
|
childpath = dir + ent->d_name;
|
|
_list_dir(childpath, files);
|
|
}
|
|
}
|
|
else {
|
|
if (suffixd.size() > 0) {
|
|
bool can_push = false;
|
|
for (int i = 0; i < (int)suffixd.size(); i++) {
|
|
if (_endswith(ent->d_name, suffixd[i]))
|
|
can_push = true;
|
|
}
|
|
if (can_push) {
|
|
absolutepath = dir + ent->d_name;
|
|
files.push_back(ent->d_name); // filepath
|
|
}
|
|
}
|
|
else {
|
|
absolutepath = dir + ent->d_name;
|
|
files.push_back(ent->d_name); // filepath
|
|
}
|
|
}
|
|
}
|
|
sort(files.begin(), files.end()); //sort names
|
|
}
|
|
|
|
vector<string> _split(const string& srcstr, const string& delimeter)
|
|
{
|
|
vector<string> ret(0); //use ret save the spilted reault
|
|
if (srcstr.empty()) //judge the arguments
|
|
{
|
|
return ret;
|
|
}
|
|
string::size_type pos_begin = srcstr.find_first_not_of(delimeter); //find first element of srcstr
|
|
|
|
string::size_type dlm_pos; //the delimeter postion
|
|
string temp; //use third-party temp to save splited element
|
|
while (pos_begin != string::npos) //if not a next of end, continue spliting
|
|
{
|
|
dlm_pos = srcstr.find(delimeter, pos_begin); //find the delimeter symbol
|
|
if (dlm_pos != string::npos)
|
|
{
|
|
temp = srcstr.substr(pos_begin, dlm_pos - pos_begin);
|
|
pos_begin = dlm_pos + delimeter.length();
|
|
}
|
|
else
|
|
{
|
|
temp = srcstr.substr(pos_begin);
|
|
pos_begin = dlm_pos;
|
|
}
|
|
if (!temp.empty())
|
|
ret.push_back(temp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool _startswith(const std::string& str, const std::string& start)
|
|
{
|
|
size_t srclen = str.size();
|
|
size_t startlen = start.size();
|
|
if (srclen >= startlen)
|
|
{
|
|
string temp = str.substr(0, startlen);
|
|
if (temp == start)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool _endswith(const std::string& str, const std::string& end)
|
|
{
|
|
size_t srclen = str.size();
|
|
size_t endlen = end.size();
|
|
if (srclen >= endlen)
|
|
{
|
|
string temp = str.substr(srclen - endlen, endlen);
|
|
if (temp == end)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int inline randint(int l, int u)
|
|
{
|
|
return l + rand() % (u - l + 1);
|
|
// rand() % (u-l+1) -> [0, u-l]
|
|
// [0, u-l] -> [l, u]
|
|
}
|
|
|
|
void _randperm(int n, int m, int arr[], bool sort_)
|
|
{
|
|
int* x = (int*)malloc(sizeof(int)*n);
|
|
for (int i = 0; i < n; ++i)
|
|
x[i] = i;
|
|
for (int i = 0; i < m; ++i)
|
|
{
|
|
int j = randint(i, n - 1);
|
|
int t = x[i]; x[i] = x[j]; x[j] = t; // swap(x[i], x[j]);
|
|
}
|
|
if (sort_)
|
|
sort(x, x + m);
|
|
for (int i = 0; i < m; ++i)
|
|
arr[i] = x[i];
|
|
free(x);
|
|
}
|
|
|
|
#define FL_PI 3.14159265358979323846f
|
|
#define FL_1_2_PI 1.57079632679f
|
|
#define FL_2__PI 6.28318530718
|
|
float _atan2(float y, float x)
|
|
{
|
|
float ang(0);
|
|
if (x > 0)
|
|
ang = atanf(y / x);
|
|
else if (y >= 0 && x < 0)
|
|
ang = atanf(y / x) + FL_PI;
|
|
else if (y < 0 && x < 0)
|
|
ang = atanf(y / x) - FL_PI;
|
|
else if (y > 0 && x == 0)
|
|
ang = FL_1_2_PI;
|
|
else if (y < 0 && x == 0)
|
|
ang = -FL_1_2_PI;
|
|
else // (y == 0 && x == 0)
|
|
ang = INFINITY;
|
|
// if (ang < 0) ang += FL_2__PI;
|
|
return ang;
|
|
}
|
|
|
|
void _mean_std(std::vector<float>& vec, float& mean, float& std)
|
|
{
|
|
float sum = std::accumulate(std::begin(vec), std::end(vec), 0.0);
|
|
mean = sum / vec.size();
|
|
|
|
float accum = 0.0;
|
|
std::for_each(std::begin(vec), std::end(vec), [&](const double d) {
|
|
accum += (d - mean)*(d - mean);
|
|
});
|
|
|
|
std = sqrt(accum / (vec.size() - 1));
|
|
}
|
|
|
|
float _get_min_angle_PI(float alpha, float beta)
|
|
{
|
|
float pi = float(CV_PI);
|
|
float pi2 = float(2.0 * CV_PI);
|
|
|
|
//normalize data in [0, 2*pi]
|
|
float a = fmod(alpha + pi2, pi2);
|
|
float b = fmod(beta + pi2, pi2);
|
|
|
|
//normalize data in [0, pi]
|
|
if (a > pi)
|
|
a -= pi;
|
|
if (b > pi)
|
|
b -= pi;
|
|
|
|
if (a > b)
|
|
{
|
|
swap(a, b);
|
|
}
|
|
|
|
float diff1 = b - a;
|
|
float diff2 = pi - diff1;
|
|
return min(diff1, diff2);
|
|
}
|
|
|
|
|
|
void _load_ellipse_GT(const string& gt_file_name, vector<Ellipse>& gt_ellipses, bool is_angle_radians)
|
|
{
|
|
ifstream in(gt_file_name);
|
|
if (!in.good())
|
|
{
|
|
cout << "Error opening: " << gt_file_name << endl;
|
|
return;
|
|
}
|
|
|
|
unsigned n;
|
|
in >> n;
|
|
|
|
gt_ellipses.clear();
|
|
gt_ellipses.reserve(n);
|
|
|
|
while (in.good() && n--)
|
|
{
|
|
Ellipse e;
|
|
in >> e.xc_ >> e.yc_ >> e.a_ >> e.b_ >> e.rad_;
|
|
|
|
if (!is_angle_radians)
|
|
{
|
|
// 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_ellipses.push_back(e);
|
|
}
|
|
in.close();
|
|
}
|
|
|
|
void _load_ellipse_DT(const string& dt_file_name, vector<Ellipse>& dt_ellipses, bool is_angle_radians)
|
|
{
|
|
ifstream in(dt_file_name);
|
|
if (!in.good())
|
|
{
|
|
cout << "Error opening: " << dt_file_name << endl;
|
|
return;
|
|
}
|
|
|
|
unsigned n;
|
|
in >> n;
|
|
|
|
dt_ellipses.clear();
|
|
dt_ellipses.reserve(n);
|
|
|
|
while (in.good() && n--)
|
|
{
|
|
Ellipse e;
|
|
in >> e.xc_ >> e.yc_ >> e.a_ >> e.b_ >> e.rad_ >> e.score_;
|
|
|
|
if (!is_angle_radians)
|
|
{
|
|
// 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;
|
|
dt_ellipses.push_back(e);
|
|
}
|
|
in.close();
|
|
}
|
|
|
|
bool _ellipse_overlap(const Mat1b& gt, const Mat1b& dt, float th)
|
|
{
|
|
float f_and = float(countNonZero(gt & dt));
|
|
float f_or = float(countNonZero(gt | dt));
|
|
float f_sim = f_and / f_or;
|
|
|
|
return (f_sim >= th);
|
|
}
|
|
|
|
float _ellipse_overlap_real(const Mat1b& gt, const Mat1b& dt)
|
|
{
|
|
float f_and = float(countNonZero(gt & dt));
|
|
float f_or = float(countNonZero(gt | dt));
|
|
float f_sim = f_and / f_or;
|
|
|
|
return f_sim;
|
|
}
|
|
|
|
int _bool_count(const std::vector<bool> vb)
|
|
{
|
|
int counter = 0;
|
|
for (unsigned i = 0; i<vb.size(); ++i)
|
|
{
|
|
if (vb[i]) ++counter;
|
|
}
|
|
return counter;
|
|
}
|
|
|
|
float _ellipse_evaluate_one(const vector<Ellipse>& ell_gt, const vector<Ellipse>& ell_dt, const Mat3b& img)
|
|
{
|
|
float threshold_overlap = 0.8f;
|
|
// float threshold = 0.95f;
|
|
|
|
unsigned sz_gt = ell_gt.size();
|
|
unsigned sz_dt = ell_dt.size();
|
|
|
|
unsigned sz_dt_min = unsigned(min(1000, int(sz_dt)));
|
|
|
|
vector<Mat1b> mat_gts(sz_gt);
|
|
vector<Mat1b> mat_dts(sz_dt_min);
|
|
|
|
// Draw each ground-Truth ellipse
|
|
for (unsigned i = 0; i<sz_gt; ++i)
|
|
{
|
|
const Ellipse& e = ell_gt[i];
|
|
|
|
Mat1b tmp(img.rows, img.cols, uchar(0));
|
|
ellipse(tmp, Point((int)e.xc_, (int)e.yc_), Size((int)e.a_, (int)e.b_), e.rad_ * 180.0 / CV_PI, 0.0, 360.0, Scalar(255), -1);
|
|
mat_gts[i] = tmp;
|
|
}
|
|
|
|
// Draw each detected ellipse
|
|
for (unsigned i = 0; i<sz_dt_min; ++i)
|
|
{
|
|
const Ellipse& e = ell_dt[i];
|
|
|
|
Mat1b tmp(img.rows, img.cols, uchar(0));
|
|
ellipse(tmp, Point((int)e.xc_, (int)e.yc_), Size((int)e.a_, (int)e.b_), e.rad_ * 180.0 / CV_PI, 0.0, 360.0, Scalar(255), -1);
|
|
mat_dts[i] = tmp;
|
|
}
|
|
|
|
Mat1b overlap(sz_gt, sz_dt_min, uchar(0));
|
|
for (int r = 0; r < overlap.rows; ++r)
|
|
{
|
|
for (int c = 0; c < overlap.cols; ++c)
|
|
{
|
|
// The proportion of overlapping areas to the true area (If biger than, assign to 255)
|
|
overlap(r, c) = _ellipse_overlap(mat_gts[r], mat_dts[c], threshold_overlap) ? uchar(255) : uchar(0);
|
|
}
|
|
}
|
|
|
|
int counter = 0;
|
|
vector<bool> vec_gt(sz_gt, false);
|
|
vector<bool> vec_dt(sz_dt_min, false);
|
|
|
|
// Each row in the matrix has one means the ellipse be found
|
|
for (unsigned int i = 0; i < sz_dt_min; ++i)
|
|
{
|
|
for (unsigned int j = 0; j < sz_gt; ++j)
|
|
{
|
|
bool b_found = overlap(j, i) != 0;
|
|
if (b_found)
|
|
{
|
|
vec_gt[j] = true;
|
|
vec_dt[i] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
float tp = _bool_count(vec_gt);
|
|
float fn = int(sz_gt) - tp;
|
|
float fp = sz_dt - _bool_count(vec_dt); // !!!! sz_dt - _bool_count(vec_dt); //
|
|
|
|
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 fmeasure;
|
|
}
|
|
|
|
float _ellipse_evaluate(vector<string>& image_fns, vector<string>& gt_fns, vector<string>& dt_fns, bool gt_angle_radians)
|
|
{
|
|
float fmeasure(0.f);
|
|
for (int i = 0; i < image_fns.size(); i++) {
|
|
Mat3b image = imread(image_fns[i]);
|
|
|
|
vector<Ellipse> ell_gt, ell_dt;
|
|
_load_ellipse_GT(gt_fns[i], ell_gt, gt_angle_radians);
|
|
_load_ellipse_DT(dt_fns[i], ell_dt);
|
|
|
|
int tp, fn, fp;
|
|
fmeasure += _ellipse_evaluate_one(ell_gt, ell_dt, image);
|
|
|
|
}
|
|
|
|
fmeasure /= image_fns.size();
|
|
return fmeasure;
|
|
}
|
|
|
|
Point2f inline _lineCrossPoint(Point2f l1p1, Point2f l1p2, Point2f l2p1, Point2f l2p2)
|
|
{
|
|
Point2f crossPoint;
|
|
float k1, k2, b1, b2;
|
|
if (l1p1.x == l1p2.x&&l2p1.x == l2p2.x) {
|
|
crossPoint = Point2f(0, 0); // invalid point
|
|
return crossPoint;
|
|
}
|
|
if (l1p1.x == l1p2.x)
|
|
{
|
|
crossPoint.x = l1p1.x;
|
|
k2 = (l2p2.y - l2p1.y) / (l2p2.x - l2p1.x);
|
|
b2 = l2p1.y - k2*l2p1.x;
|
|
crossPoint.y = k2*crossPoint.x + b2;
|
|
return crossPoint;
|
|
}
|
|
if (l2p1.x == l2p2.x)
|
|
{
|
|
crossPoint.x = l2p1.x;
|
|
k2 = (l1p2.y - l1p1.y) / (l1p2.x - l1p1.x);
|
|
b2 = l1p1.y - k2*l1p1.x;
|
|
crossPoint.y = k2*crossPoint.x + b2;
|
|
return crossPoint;
|
|
}
|
|
|
|
k1 = (l1p2.y - l1p1.y) / (l1p2.x - l1p1.x);
|
|
k2 = (l2p2.y - l2p1.y) / (l2p2.x - l2p1.x);
|
|
b1 = l1p1.y - k1*l1p1.x;
|
|
b2 = l2p1.y - k2*l2p1.x;
|
|
if (k1 == k2)
|
|
{
|
|
crossPoint = Point2f(0, 0); // invalid point
|
|
}
|
|
else
|
|
{
|
|
crossPoint.x = (b2 - b1) / (k1 - k2);
|
|
crossPoint.y = k1*crossPoint.x + b1;
|
|
}
|
|
return crossPoint;
|
|
}
|
|
|
|
void inline _point2Mat(Point2f p1, Point2f p2, float mat[2][2])
|
|
{
|
|
mat[0][0] = p1.x;
|
|
mat[0][1] = p1.y;
|
|
mat[1][0] = p2.x;
|
|
mat[1][1] = p2.y;
|
|
}
|
|
|
|
float _value4SixPoints(cv::Point2f p3, cv::Point2f p2, cv::Point2f p1, cv::Point2f p4, cv::Point2f p5, cv::Point2f p6)
|
|
{
|
|
float result = 1;
|
|
Mat A, B, C;
|
|
float matB[2][2], matC[2][2];
|
|
Point2f v, w, u;
|
|
v = _lineCrossPoint(p1, p2, p3, p4);
|
|
w = _lineCrossPoint(p5, p6, p3, p4);
|
|
u = _lineCrossPoint(p5, p6, p1, p2);
|
|
|
|
_point2Mat(u, v, matB);
|
|
_point2Mat(p1, p2, matC);
|
|
B = Mat(2, 2, CV_32F, matB);
|
|
C = Mat(2, 2, CV_32F, matC);
|
|
A = C*B.inv();
|
|
|
|
// cout<<"u:\t"<<u<<endl;
|
|
// cout<<"v:\t"<<v<<endl;
|
|
// cout<<"B:\t"<<B<<endl;
|
|
// cout<<A<<endl;
|
|
|
|
result *= A.at<float>(0, 0)*A.at<float>(1, 0) / (A.at<float>(0, 1)*A.at<float>(1, 1));
|
|
|
|
_point2Mat(p3, p4, matC);
|
|
_point2Mat(v, w, matB);
|
|
B = Mat(2, 2, CV_32F, matB);
|
|
C = Mat(2, 2, CV_32F, matC);
|
|
A = C*B.inv();
|
|
result *= A.at<float>(0, 0)*A.at<float>(1, 0) / (A.at<float>(0, 1)*A.at<float>(1, 1));
|
|
|
|
_point2Mat(p5, p6, matC);
|
|
_point2Mat(w, u, matB);
|
|
B = Mat(2, 2, CV_32F, matB);
|
|
C = Mat(2, 2, CV_32F, matC);
|
|
A = C*B.inv();
|
|
result *= A.at<float>(0, 0)*A.at<float>(1, 0) / (A.at<float>(0, 1)*A.at<float>(1, 1));
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/** Compute ellipse foci, given ellipse params.
|
|
*/
|
|
void _ellipse_foci(float *param, float *foci)
|
|
{
|
|
float f;
|
|
/* check parameters */
|
|
if (param == NULL) fprintf(stderr, "ellipse_foci: invalid input ellipse.");
|
|
if (foci == NULL) fprintf(stderr, "ellipse_foci: 'foci' must be non null.");
|
|
|
|
f = sqrt(param[2] * param[2] - param[3] * param[3]);
|
|
foci[0] = param[0] + f * cos(param[4]);
|
|
foci[1] = param[1] + f * sin(param[4]);
|
|
foci[2] = param[0] - f * cos(param[4]);
|
|
foci[3] = param[1] - f * sin(param[4]);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/** Signed angle difference.
|
|
*/
|
|
float angle_diff_signed(float a, float b)
|
|
{
|
|
a -= b;
|
|
while (a <= -M_PI) a += M_2__PI;
|
|
while (a > M_PI) a -= M_2__PI;
|
|
return a;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/** Absolute value angle difference.
|
|
*/
|
|
float _angle_diff(float a, float b)
|
|
{
|
|
a -= b;
|
|
while (a <= -M_PI) a += M_2__PI;
|
|
while (a > M_PI) a -= M_2__PI;
|
|
if (a < 0.0) a = -a;
|
|
return a;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/** Compute the angle of the normal to a point belonging to an ellipse
|
|
using the focal property.
|
|
*/
|
|
float _ellipse_normal_angle(float x, float y, float *foci)
|
|
{
|
|
float tmp1, tmp2, tmp3, theta;
|
|
/* check parameters */
|
|
if (foci == NULL) fprintf(stderr, "ellipse_normal_angle: 'foci' must be non null");
|
|
|
|
tmp1 = atan2(y - foci[1], x - foci[0]);
|
|
tmp2 = atan2(y - foci[3], x - foci[2]);
|
|
tmp3 = angle_diff_signed(tmp1, tmp2);
|
|
|
|
theta = tmp1 - tmp3 / 2.0;
|
|
while (theta <= -M_PI) theta += M_2__PI;
|
|
while (theta > M_PI) theta -= M_2__PI;
|
|
return theta;
|
|
}
|
|
|
|
|
|
void cv_canny(cv::Mat& src, cv::Mat& dst,
|
|
cv::Mat& dx, cv::Mat& dy,
|
|
int aperture_size, bool L2gradient, double percent_ne) {
|
|
|
|
cv::AutoBuffer<char> buffer;
|
|
std::vector<uchar*> stack;
|
|
uchar **stack_top = 0, **stack_bottom = 0;
|
|
|
|
if (CV_MAT_TYPE(src.type()) != CV_8UC1 ||
|
|
CV_MAT_TYPE(dst.type()) != CV_8UC1 ||
|
|
CV_MAT_TYPE(dx.type()) != CV_16SC1 ||
|
|
CV_MAT_TYPE(dy.type()) != CV_16SC1)
|
|
CV_Error(CV_StsUnsupportedFormat, "");
|
|
|
|
if (!CV_ARE_SIZES_EQ(&src, &dst))
|
|
CV_Error(CV_StsUnmatchedSizes, "");
|
|
|
|
aperture_size &= INT_MAX;
|
|
if ((aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7)
|
|
CV_Error(CV_StsBadFlag, "");
|
|
|
|
int i, j;
|
|
CvSize size;
|
|
size.width = src.cols;
|
|
size.height = src.rows;
|
|
|
|
//cv::Sobel(src, dx, 1, 0, aperture_size);
|
|
//cv::Sobel(src, dy, 0, 1, aperture_size);
|
|
cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size);
|
|
cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size);
|
|
|
|
// double min, max;
|
|
// cv::minMaxLoc(Mat(dx->rows, dx->cols, CV_16SC1, dx->data.fl), &min, &max);
|
|
// cout << "min: " << min << ", max: " << max << endl;
|
|
|
|
Mat1f magGrad(size.height, size.width, 0.f);
|
|
float maxGrad(0);
|
|
float val(0);
|
|
for (i = 0; i < size.height; ++i)
|
|
{
|
|
float* _pmag = magGrad.ptr<float>(i);
|
|
//const short* _dx = (short*)(dx.data.ptr + dx.step*i);
|
|
//const short* _dy = (short*)(dy.data.ptr + dy.step*i);
|
|
for (j = 0; j < size.width; ++j)
|
|
{
|
|
//val = float(abs(_dx[j]) + abs(_dy[j]));
|
|
val = float(abs(dx.at<short>(i, j)) + abs(dy.at<short>(i, j)));
|
|
_pmag[j] = val;
|
|
maxGrad = (val > maxGrad) ? val : maxGrad;
|
|
}
|
|
}
|
|
// cout << "maxGrad: " << maxGrad << endl;
|
|
|
|
// set magic numbers
|
|
const int NUM_BINS = 64;
|
|
const double percent_of_pixels_not_edges = percent_ne;
|
|
const double threshold_ratio = 0.3;
|
|
|
|
// compute histogram
|
|
int bin_size = cvFloor(maxGrad / float(NUM_BINS) + 0.5f) + 1;
|
|
if (bin_size < 1) bin_size = 1;
|
|
int bins[NUM_BINS] = { 0 };
|
|
for (i = 0; i < size.height; ++i)
|
|
{
|
|
float *_pmag = magGrad.ptr<float>(i);
|
|
for (j = 0; j < size.width; ++j)
|
|
{
|
|
int hgf = int(_pmag[j]);
|
|
bins[int(_pmag[j]) / bin_size]++;
|
|
}
|
|
}
|
|
// for (int i = 0; i < NUM_BINS; i++)
|
|
// cout << "BIN " << i << " :" << bins[i] << endl;
|
|
|
|
// Select the thresholds
|
|
float total(0.f);
|
|
float target = float(size.height * size.width * percent_of_pixels_not_edges);
|
|
int low_thresh, high_thresh(0);
|
|
|
|
while (total < target)
|
|
{
|
|
total += bins[high_thresh];
|
|
high_thresh++;
|
|
}
|
|
high_thresh *= bin_size;
|
|
low_thresh = cvFloor(threshold_ratio * float(high_thresh));
|
|
// cout << "low_thresh: " << low_thresh << ", high_thresh: " << high_thresh << endl;
|
|
|
|
int low, high, maxsize;
|
|
int* mag_buf[3];
|
|
uchar* map;
|
|
ptrdiff_t mapstep;
|
|
|
|
if (L2gradient) {
|
|
Cv32suf ul, uh;
|
|
ul.f = (float)low_thresh;
|
|
uh.f = (float)high_thresh;
|
|
|
|
low = ul.i;
|
|
high = uh.i;
|
|
}
|
|
else {
|
|
low = cvFloor(low_thresh);
|
|
high = cvFloor(high_thresh);
|
|
}
|
|
|
|
buffer.allocate((size.width + 2)*(size.height + 2) + (size.width + 2) * 3 * sizeof(int));
|
|
// cout << sizeof(int) << ", " << (size.width + 2)*(size.height + 2) + (size.width + 2) * 3 * sizeof(int) << endl;
|
|
mag_buf[0] = (int*)(char*)buffer;
|
|
mag_buf[1] = mag_buf[0] + size.width + 2;
|
|
mag_buf[2] = mag_buf[1] + size.width + 2;
|
|
map = (uchar*)(mag_buf[2] + size.width + 2);
|
|
mapstep = size.width + 2;
|
|
|
|
maxsize = MAX(1 << 10, size.width*size.height / 10);
|
|
stack.resize(maxsize);
|
|
stack_top = stack_bottom = &stack[0];
|
|
|
|
memset(mag_buf[0], 0, (size.width + 2) * sizeof(int));
|
|
memset(map, 1, mapstep);
|
|
memset(map + mapstep*(size.height + 1), 1, mapstep);
|
|
|
|
/* sector numbers
|
|
(Top-Left Origin)
|
|
|
|
1 2 3
|
|
* * *
|
|
* * *
|
|
0*******0
|
|
* * *
|
|
* * *
|
|
3 2 1
|
|
*/
|
|
|
|
#define CANNY_PUSH(d) *(d) = (uchar)2, *stack_top++ = (d)
|
|
#define CANNY_POP(d) (d) = *--stack_top
|
|
|
|
CvMat mag_row = cvMat(1, size.width, CV_32F);
|
|
|
|
// Mat push_show = Mat::zeros(size.height+1, size.width+1, CV_8U);
|
|
|
|
// calculate magnitude and angle of gradient, perform non-maxima supression.
|
|
// fill the map with one of the following values:
|
|
// 0 - the pixel might belong to an edge
|
|
// 1 - the pixel can not belong to an edge
|
|
// 2 - the pixel does belong to an edge
|
|
for (i = 0; i <= size.height; i++)
|
|
{
|
|
int* _mag = mag_buf[(i > 0) + 1] + 1;
|
|
float* _magf = (float*)_mag;
|
|
//const short* _dx = (short*)(dx->data.ptr + dx->step*i);
|
|
//const short* _dy = (short*)(dy->data.ptr + dy->step*i);
|
|
uchar* _map;
|
|
int x, y;
|
|
ptrdiff_t magstep1, magstep2;
|
|
int prev_flag = 0;
|
|
|
|
if (i < size.height)
|
|
{
|
|
_mag[-1] = _mag[size.width] = 0;
|
|
|
|
if (!L2gradient)
|
|
for (j = 0; j < size.width; j++)
|
|
_mag[j] = abs(dx.at<short>(i, j)) + abs(dy.at<short>(i, j));
|
|
else
|
|
{
|
|
for (j = 0; j < size.width; j++)
|
|
{
|
|
x = dx.at<short>(i, j); y = dy.at<short>(i, j);
|
|
_magf[j] = (float)std::sqrt((double)x*x + (double)y*y);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
memset(_mag - 1, 0, (size.width + 2) * sizeof(int));
|
|
|
|
// at the very beginning we do not have a complete ring
|
|
// buffer of 3 magnitude rows for non-maxima suppression
|
|
if (i == 0)
|
|
continue;
|
|
|
|
_map = map + mapstep*i + 1;
|
|
_map[-1] = _map[size.width] = 1;
|
|
|
|
_mag = mag_buf[1] + 1; // take the central row
|
|
//_dx = (short*)(dx->data.ptr + dx->step*(i - 1));
|
|
//_dy = (short*)(dy->data.ptr + dy->step*(i - 1));
|
|
|
|
magstep1 = mag_buf[2] - mag_buf[1];
|
|
magstep2 = mag_buf[0] - mag_buf[1];
|
|
|
|
if ((stack_top - stack_bottom) + size.width > maxsize)
|
|
{
|
|
int sz = (int)(stack_top - stack_bottom);
|
|
maxsize = MAX(maxsize * 3 / 2, maxsize + 8);
|
|
stack.resize(maxsize);
|
|
stack_bottom = &stack[0];
|
|
stack_top = stack_bottom + sz;
|
|
}
|
|
|
|
#define CANNY_SHIFT 15
|
|
#define TG22 (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5)
|
|
|
|
for (j = 0; j < size.width; j++)
|
|
{
|
|
x = dx.at<short>(i, j);
|
|
y = dy.at<short>(i, j);
|
|
int s = x ^ y; // XOR
|
|
int m = _mag[j];
|
|
|
|
x = abs(x);
|
|
y = abs(y);
|
|
if (m > low)
|
|
{
|
|
int tg22x = x * TG22;
|
|
int tg67x = tg22x + ((x + x) << CANNY_SHIFT);
|
|
int tmp = 1 << CANNY_SHIFT;
|
|
y <<= CANNY_SHIFT;
|
|
|
|
if (y < tg22x) {
|
|
if (m > _mag[j - 1] && m >= _mag[j + 1]) {
|
|
if (m > high && !prev_flag && _map[j - mapstep] != 2) {
|
|
CANNY_PUSH(_map + j); // push_show.at<uchar>(i, j) = 255;
|
|
prev_flag = 1;
|
|
}
|
|
else {
|
|
_map[j] = (uchar)0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else if (y > tg67x) {
|
|
if (m > _mag[j + magstep2] && m >= _mag[j + magstep1]) {
|
|
if (m > high && !prev_flag && _map[j - mapstep] != 2) {
|
|
CANNY_PUSH(_map + j); // push_show.at<uchar>(i, j) = 255;
|
|
prev_flag = 1;
|
|
}
|
|
else {
|
|
_map[j] = (uchar)0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
s = s < 0 ? -1 : 1;
|
|
if (m > _mag[j + magstep2 - s] && m > _mag[j + magstep1 + s]) {
|
|
if (m > high && !prev_flag && _map[j - mapstep] != 2) {
|
|
CANNY_PUSH(_map + j); // push_show.at<uchar>(i, j) = 255;
|
|
prev_flag = 1;
|
|
}
|
|
else {
|
|
_map[j] = (uchar)0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
prev_flag = 0;
|
|
_map[j] = (uchar)1;
|
|
}
|
|
|
|
// scroll the ring buffer
|
|
_mag = mag_buf[0];
|
|
mag_buf[0] = mag_buf[1];
|
|
mag_buf[1] = mag_buf[2];
|
|
mag_buf[2] = _mag;
|
|
}
|
|
|
|
// imshow("mag", push_show); waitKey();
|
|
// now track the edges (hysteresis thresholding)
|
|
while (stack_top > stack_bottom)
|
|
{
|
|
uchar* m;
|
|
if ((stack_top - stack_bottom) + 8 > maxsize)
|
|
{
|
|
int sz = (int)(stack_top - stack_bottom);
|
|
maxsize = MAX(maxsize * 3 / 2, maxsize + 8);
|
|
stack.resize(maxsize);
|
|
stack_bottom = &stack[0];
|
|
stack_top = stack_bottom + sz;
|
|
}
|
|
|
|
CANNY_POP(m);
|
|
|
|
if (!m[-1])
|
|
CANNY_PUSH(m - 1);
|
|
if (!m[1])
|
|
CANNY_PUSH(m + 1);
|
|
if (!m[-mapstep - 1])
|
|
CANNY_PUSH(m - mapstep - 1);
|
|
if (!m[-mapstep])
|
|
CANNY_PUSH(m - mapstep);
|
|
if (!m[-mapstep + 1])
|
|
CANNY_PUSH(m - mapstep + 1);
|
|
if (!m[mapstep - 1])
|
|
CANNY_PUSH(m + mapstep - 1);
|
|
if (!m[mapstep])
|
|
CANNY_PUSH(m + mapstep);
|
|
if (!m[mapstep + 1])
|
|
CANNY_PUSH(m + mapstep + 1);
|
|
}
|
|
|
|
// the final pass, form the final image
|
|
for (i = 0; i < size.height; i++) {
|
|
const uchar* _map = map + mapstep*(i + 1) + 1;
|
|
//uchar* _dst = dst->data.ptr + dst->step*i;
|
|
|
|
for (j = 0; j < size.width; j++) {
|
|
// if (_map[j] == 2)
|
|
// cout << (int)_map[j] << ", " << (int)(_map[j] >> 1) << ", " << (int)(uchar)-(_map[j] >> 1) << endl;
|
|
dst.at<uchar>(i, j) = (uchar)-(_map[j] >> 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _tag_canny(InputArray image, OutputArray _edges,
|
|
OutputArray _sobel_x, OutputArray _sobel_y,
|
|
int apertureSize, bool L2gradient, double percent_ne) {
|
|
|
|
Mat src = image.getMat();
|
|
_edges.create(src.size(), CV_8U);
|
|
_sobel_x.create(src.size(), CV_16S);
|
|
_sobel_y.create(src.size(), CV_16S);
|
|
|
|
Mat c_src = src;
|
|
Mat c_dst = _edges.getMat();
|
|
Mat c_dx = _sobel_x.getMat();
|
|
Mat c_dy = _sobel_y.getMat();
|
|
|
|
cv_canny(c_src, c_dst,
|
|
c_dx, c_dy,
|
|
apertureSize, L2gradient, percent_ne);
|
|
}
|
|
|
|
void _find_contours_eight(cv::Mat1b& image, std::vector<VVP>& segments, int iMinLength)
|
|
{
|
|
vector<Mat1b> ims; ims.resize(8);
|
|
for (int i = 0; i < 8; i++) {
|
|
ims[i] = Mat1b::zeros(image.size());
|
|
}
|
|
for (int r = 0; r < image.rows; r++) {
|
|
uchar* _e8 = image.ptr<uchar>(r);
|
|
vector<uchar*> _es; _es.resize(8);
|
|
for (int i = 0; i < 8; i++)
|
|
_es[i] = ims[i].ptr<uchar>(r);
|
|
|
|
for (int c = 0; c < image.cols; c++) {
|
|
for (int i = 0; i < 8; i++) {
|
|
if (_e8[c] == (uchar)(i + 1)) {
|
|
_es[i][c] = (uchar)255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
segments.resize(8);
|
|
for (int i = 0; i < 8; i++) {
|
|
_tag_find_contours(ims[i], segments[i], iMinLength);
|
|
}
|
|
}
|
|
|
|
void _tag_find_contours(cv::Mat1b& image, VVP& segments, int iMinLength) {
|
|
#define RG_STACK_SIZE 8192*4
|
|
|
|
|
|
// use stack to speed up the processing of global (even at the expense of memory occupied)
|
|
int stack2[RG_STACK_SIZE];
|
|
#define RG_PUSH2(a) (stack2[sp2] = (a) , sp2++)
|
|
#define RG_POP2(a) (sp2-- , (a) = stack2[sp2])
|
|
|
|
// use stack to speed up the processing of global (even at the expense of memory occupied)
|
|
Point stack3[RG_STACK_SIZE];
|
|
#define RG_PUSH3(a) (stack3[sp3] = (a) , sp3++)
|
|
#define RG_POP3(a) (sp3-- , (a) = stack3[sp3])
|
|
|
|
int i, w, h, iDim;
|
|
int x, y;
|
|
int x2, y2;
|
|
int sp2; // stack pointer
|
|
int sp3;
|
|
|
|
Mat_<uchar> src = image.clone();
|
|
w = src.cols;
|
|
h = src.rows;
|
|
iDim = w*h;
|
|
|
|
Point point;
|
|
for (y = 0; y<h; ++y)
|
|
{
|
|
for (x = 0; x<w; ++x)
|
|
{
|
|
if ((src(y, x)) != 0) // found labelled point
|
|
{
|
|
// per point
|
|
sp2 = 0;
|
|
i = x + y*w;
|
|
RG_PUSH2(i);
|
|
|
|
// empty the list of points
|
|
sp3 = 0;
|
|
while (sp2>0)
|
|
{ // rg traditional
|
|
RG_POP2(i);
|
|
x2 = i%w;
|
|
y2 = i / w;
|
|
|
|
point.x = x2;
|
|
point.y = y2;
|
|
|
|
if (src(y2, x2))
|
|
{
|
|
RG_PUSH3(point);
|
|
src(y2, x2) = 0;
|
|
}
|
|
|
|
// insert the new points in the stack only if there are
|
|
// and they are points labelled
|
|
|
|
// 4 connected
|
|
// left
|
|
if (x2>0 && (src(y2, x2 - 1) != 0))
|
|
RG_PUSH2(i - 1);
|
|
// up
|
|
if (y2>0 && (src(y2 - 1, x2) != 0))
|
|
RG_PUSH2(i - w);
|
|
// down
|
|
if (y2<h - 1 && (src(y2 + 1, x2) != 0))
|
|
RG_PUSH2(i + w);
|
|
// right
|
|
if (x2<w - 1 && (src(y2, x2 + 1) != 0))
|
|
RG_PUSH2(i + 1);
|
|
|
|
// 8 connected
|
|
if (x2>0 && y2>0 && (src(y2 - 1, x2 - 1) != 0))
|
|
RG_PUSH2(i - w - 1);
|
|
if (x2>0 && y2<h - 1 && (src(y2 + 1, x2 - 1) != 0))
|
|
RG_PUSH2(i + w - 1);
|
|
if (x2<w - 1 && y2>0 && (src(y2 - 1, x2 + 1) != 0))
|
|
RG_PUSH2(i - w + 1);
|
|
if (x2<w - 1 && y2<h - 1 && (src(y2 + 1, x2 + 1) != 0))
|
|
RG_PUSH2(i + w + 1);
|
|
|
|
}
|
|
|
|
if (sp3 >= iMinLength)
|
|
{
|
|
vector<Point> component;
|
|
component.reserve(sp3);
|
|
|
|
// push it to the points
|
|
for (i = 0; i<sp3; i++) {
|
|
// push it
|
|
component.push_back(stack3[i]);
|
|
}
|
|
segments.push_back(component);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void _find_contours_oneway(cv::Mat1b& image, VVP& segments, int iMinLength) {
|
|
|
|
int w, h, iDim;
|
|
int x, y;
|
|
int x2, y2;
|
|
int sp1, sp2; // stack pointer
|
|
float ang, ang_d, ang_t, ang_i, ang_dmin;
|
|
float theta_s, theta_i;
|
|
int xn, yn;
|
|
|
|
Mat_<uchar> src = image.clone();
|
|
w = src.cols;
|
|
h = src.rows;
|
|
iDim = w*h;
|
|
|
|
Point point;
|
|
for (y = 0; y<h; ++y)
|
|
{
|
|
for (x = 0; x<w; ++x)
|
|
{
|
|
if ((src(y, x)) != 0) // found labelled point
|
|
{
|
|
// per point
|
|
src(y, x) = 0;
|
|
|
|
vector<Point> component;
|
|
point.x = x;
|
|
point.y = y;
|
|
component.push_back(point);
|
|
x2 = x;
|
|
y2 = y;
|
|
|
|
bool found = true;
|
|
sp2 = 0;
|
|
while (found && component.size() < 3)
|
|
{
|
|
found = false;
|
|
if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
|
|
{
|
|
src(y2 + 1, x2 - 1) = 0; if (!found) { found = true; point.x = x2 - 1; point.y = y2 + 1; component.push_back(point); }
|
|
}
|
|
if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
|
|
{
|
|
src(y2 + 1, x2 + 1) = 0; if (!found) { found = true; point.x = x2 + 1; point.y = y2 + 1; component.push_back(point); }
|
|
}
|
|
if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
|
|
{
|
|
src(y2 + 1, x2) = 0; if (!found) { found = true; point.x = x2; point.y = y2 + 1; component.push_back(point); }
|
|
}
|
|
if (x2 > 0 && (src(y2, x2 - 1) != 0))
|
|
{
|
|
src(y2, x2 - 1) = 0; if (!found) { found = true; point.x = x2 - 1; point.y = y2; component.push_back(point); }
|
|
}
|
|
if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
|
|
{
|
|
src(y2, x2 + 1) = 0; if (!found) { found = true; point.x = x2 + 1; point.y = y2; component.push_back(point); }
|
|
}
|
|
if (x2 > 0 && y2 > 0 && (src(y2 - 1, x2 - 1) != 0))
|
|
{
|
|
src(y2 - 1, x2 - 1) = 0; if (!found) { found = true; point.x = x2 - 1; point.y = y2 - 1; component.push_back(point); }
|
|
}
|
|
if (x2 < w - 1 && y2 > 0 && (src(y2 - 1, x2 + 1) != 0))
|
|
{
|
|
src(y2 - 1, x2 + 1) = 0; if (!found) { found = true; point.x = x2 + 1; point.y = y2 - 1; component.push_back(point); }
|
|
}
|
|
if (y2 > 0 && (src(y2 - 1, x2) != 0))
|
|
{
|
|
src(y2 - 1, x2) = 0; if (!found) { found = true; point.x = x2; point.y = y2 - 1; component.push_back(point); }
|
|
}
|
|
if (found)
|
|
{
|
|
sp2++; x2 = component[sp2].x; y2 = component[sp2].y;
|
|
}
|
|
}
|
|
|
|
if (component.size() < 3) continue;
|
|
ang = _atan2(component[2].y - component[0].y, component[2].x - component[0].x);
|
|
sp1 = 0;
|
|
|
|
found = true;
|
|
while (found)
|
|
{
|
|
ang_dmin = 1e3;
|
|
found = false;
|
|
if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 + 1 - component[sp1].y, x2 - 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 + 1, x2 - 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 - 1; yn = y2 + 1; }
|
|
}
|
|
if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 + 1 - component[sp1].y, x2 + 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 + 1, x2 + 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 + 1; yn = y2 + 1; }
|
|
}
|
|
if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
|
|
{
|
|
ang_t = _atan2(y2 + 1 - component[sp1].y, x2 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 + 1, x2) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2; yn = y2 + 1; }
|
|
}
|
|
if (x2 > 0 && (src(y2, x2 - 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 - component[sp1].y, x2 - 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2, x2 - 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 - 1; yn = y2; }
|
|
}
|
|
if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 - component[sp1].y, x2 + 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2, x2 + 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 + 1; yn = y2; }
|
|
}
|
|
if (x2 > 0 && y2 > 0 && (src(y2 - 1, x2 - 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 - 1 - component[sp1].y, x2 - 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 - 1, x2 - 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 - 1; yn = y2 - 1; }
|
|
}
|
|
if (x2 < w - 1 && y2 > 0 && (src(y2 - 1, x2 + 1) != 0))
|
|
{
|
|
ang_t = _atan2(y2 - 1 - component[sp1].y, x2 + 1 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 - 1, x2 + 1) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2 + 1; yn = y2 - 1; }
|
|
}
|
|
if (y2 > 0 && (src(y2 - 1, x2) != 0))
|
|
{
|
|
ang_t = _atan2(y2 - 1 - component[sp1].y, x2 - component[sp1].x);
|
|
ang_d = abs(ang - ang_t);
|
|
src(y2 - 1, x2) = 0; if (ang_d < ang_dmin) { ang_dmin = ang_d; ang_i = ang_t; xn = x2; yn = y2 - 1; }
|
|
}
|
|
if (ang_dmin < M_1_2_PI)
|
|
{
|
|
found = true; point.x = xn; point.y = yn; component.push_back(point);
|
|
x2 = xn;
|
|
y2 = yn;
|
|
sp1++; sp2++;
|
|
if (sp2 >= 9 && sp2 % 3 == 0)
|
|
{
|
|
float a1 = _atan2(component[sp2 - 6].y - component[sp2 - 9].y, component[sp2 - 6].x - component[sp2 - 9].x);
|
|
float a2 = _atan2(component[sp2 - 3].y - component[sp2 - 6].y, component[sp2 - 3].x - component[sp2 - 6].x);
|
|
theta_s = a1 - a2;
|
|
|
|
a1 = _atan2(component[sp2 - 3].y - component[sp2 - 6].y, component[sp2 - 3].x - component[sp2 - 6].x);
|
|
a2 = _atan2(component[sp2].y - component[sp2 - 3].y, component[sp2].x - component[sp2 - 3].x);
|
|
theta_i = a1 - a2;
|
|
if (abs(theta_s - theta_i) > 0.6) break;
|
|
}
|
|
ang = ang_i;
|
|
}
|
|
}
|
|
|
|
if (component.size() >= iMinLength)
|
|
{
|
|
segments.push_back(component);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void _tag_show_contours(cv::Mat1b& image, VVP& segments, const char* title)
|
|
{
|
|
Mat3b contoursIm(image.rows, image.cols, Vec3b(0, 0, 0));
|
|
for (size_t i = 0; i<segments.size(); ++i)
|
|
{
|
|
Vec3b color(rand() % 255, 128 + rand() % 127, 128 + rand() % 127);
|
|
for (size_t j = 0; j<segments[i].size(); ++j)
|
|
contoursIm(segments[i][j]) = color;
|
|
}
|
|
imshow(title, contoursIm);
|
|
}
|
|
|
|
void _tag_show_contours(cv::Size& imsz, VVP& segments, const char* title)
|
|
{
|
|
Mat3b contoursIm(imsz, Vec3b(0, 0, 0));
|
|
for (size_t i = 0; i<segments.size(); ++i)
|
|
{
|
|
Vec3b color(rand() % 255, 128 + rand() % 127, 128 + rand() % 127);
|
|
for (size_t j = 0; j<segments[i].size(); ++j)
|
|
contoursIm(segments[i][j]) = color;
|
|
}
|
|
imshow(title, contoursIm);
|
|
}
|
|
|
|
void _show_contours_eight(cv::Mat1b& image, std::vector<VVP>& segments8, const char* title)
|
|
{
|
|
Mat3b contoursIm(image.rows, image.cols, Vec3b(0, 0, 0));
|
|
for (size_t i = 0; i < segments8.size(); ++i)
|
|
{
|
|
Vec3b color(rand() % 255, 128 + rand() % 127, 128 + rand() % 127);
|
|
for (size_t j = 0; j < segments8[i].size(); ++j)
|
|
{
|
|
for (size_t k = 0; k < segments8[i][j].size(); ++k)
|
|
contoursIm(segments8[i][j][k]) = color;
|
|
}
|
|
}
|
|
imshow(title, contoursIm);
|
|
}
|
|
|
|
bool _SortBottomLeft2TopRight(const Point& lhs, const Point& rhs)
|
|
{
|
|
if (lhs.x == rhs.x)
|
|
{
|
|
return lhs.y > rhs.y;
|
|
}
|
|
return lhs.x < rhs.x;
|
|
}
|
|
|
|
bool _SortBottomLeft2TopRight2f(const Point2f& lhs, const Point2f& rhs)
|
|
{
|
|
if (lhs.x == rhs.x)
|
|
{
|
|
return lhs.y > rhs.y;
|
|
}
|
|
return lhs.x < rhs.x;
|
|
}
|
|
|
|
bool _SortTopLeft2BottomRight(const Point& lhs, const Point& rhs)
|
|
{
|
|
if (lhs.x == rhs.x)
|
|
{
|
|
return lhs.y < rhs.y;
|
|
}
|
|
return lhs.x < rhs.x;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// #define DEBUG_SPEED
|
|
// #define DEBUG_ELLFIT
|
|
// #define DEBUG_PREPROCESSING
|
|
|
|
// #define DISCARD_TCN
|
|
#define DISCARD_TCN2
|
|
#define DISCARD_CONSTRAINT_OBOX
|
|
|
|
// #define DISCARD_CONSTRAINT_CONVEXITY
|
|
// #define DISCARD_CONSTRAINT_POSITION
|
|
#define CONSTRAINT_CNC_1
|
|
// #define CONSTRAINT_CNC_2
|
|
// #define CONSTRAINT_CNC_3
|
|
// #define DISCARD_CONSTRAINT_CENTER
|
|
|
|
// #define T_CNC 0.2f
|
|
// #define T_TCN_L 0.4f // filter lines
|
|
// #define T_TCN_P 0.6f
|
|
|
|
// #define Thre_r 0.2f
|
|
|
|
|
|
void _concate_arcs(VP& arc1, VP& arc2, VP& arc3, VP& arc)
|
|
{
|
|
for (int i = 0; i < arc1.size(); i++)
|
|
arc.push_back(arc1[i]);
|
|
for (int i = 0; i < arc2.size(); i++)
|
|
arc.push_back(arc2[i]);
|
|
for (int i = 0; i < arc3.size(); i++)
|
|
arc.push_back(arc3[i]);
|
|
}
|
|
|
|
void _concate_arcs(VP& arc1, VP& arc2, VP& arc)
|
|
{
|
|
for (int i = 0; i < arc1.size(); i++)
|
|
arc.push_back(arc1[i]);
|
|
for (int i = 0; i < arc2.size(); i++)
|
|
arc.push_back(arc2[i]);
|
|
}
|
|
|
|
EllipseDetector::EllipseDetector(void)
|
|
{
|
|
// Default Parameters Settings
|
|
szPreProcessingGaussKernel_ = Size(5, 5);
|
|
dPreProcessingGaussSigma_ = 1.0;
|
|
fThrArcPosition_ = 1.0f;
|
|
fMaxCenterDistance_ = 100.0f * 0.05f;
|
|
fMaxCenterDistance2_ = fMaxCenterDistance_ * fMaxCenterDistance_;
|
|
iMinEdgeLength_ = 16;
|
|
fMinOrientedRectSide_ = 3.0f;
|
|
fDistanceToEllipseContour_ = 0.1f;
|
|
fMinScore_ = 0.7f;
|
|
fMinReliability_ = 0.5f;
|
|
uNs_ = 16;
|
|
dPercentNe_ = 0.9;
|
|
|
|
fT_CNC_ = 0.2f;
|
|
fT_TCN_L_ = 0.4f; // filter lines
|
|
fT_TCN_P_ = 0.6f;
|
|
fThre_r_ = 0.2f;
|
|
|
|
srand(unsigned(time(NULL)));
|
|
}
|
|
|
|
EllipseDetector::~EllipseDetector(void)
|
|
{
|
|
}
|
|
|
|
void EllipseDetector::SetParameters(Size szPreProcessingGaussKernel,
|
|
double dPreProcessingGaussSigma, // 高斯模糊,去噪声
|
|
float fThPosition, // 位置约束,阈值,低于阈值不要
|
|
float fMaxCenterDistance, // 中心点约束
|
|
int iMinEdgeLength, // 最小弧度常识,一般 8-16, 小于的弧不要
|
|
float fMinOrientedRectSide, // 筛选弧参数,最小外接矩形,短边长度
|
|
float fDistanceToEllipseContour, // fitting得分d, 一般几个像素
|
|
float fMinScore, // 0.5 - 0.7, 最终椭圆得分阈值
|
|
float fMinReliability, // 弧长与椭圆周长比值阈值
|
|
int iNs, // 中心性约数, 2个弧中心与椭圆中心距离 一般22
|
|
double dPercentNe, // 边缘检测阈值, 越大保留的弧数量越多 0.9 - 0.99
|
|
float fT_CNC, // 判读集合CNC, 一般不会调
|
|
float fT_TCN_L,
|
|
float fT_TCN_P,
|
|
float fThre_r
|
|
)
|
|
{
|
|
szPreProcessingGaussKernel_ = szPreProcessingGaussKernel;
|
|
dPreProcessingGaussSigma_ = dPreProcessingGaussSigma;
|
|
fThrArcPosition_ = fThPosition;
|
|
fMaxCenterDistance_ = fMaxCenterDistance;
|
|
iMinEdgeLength_ = iMinEdgeLength;
|
|
fMinOrientedRectSide_ = fMinOrientedRectSide;
|
|
fDistanceToEllipseContour_ = fDistanceToEllipseContour;
|
|
fMinScore_ = fMinScore;
|
|
fMinReliability_ = fMinReliability;
|
|
uNs_ = iNs;
|
|
dPercentNe_ = dPercentNe;
|
|
|
|
fT_CNC_ = fT_CNC;
|
|
fT_TCN_L_ = fT_TCN_L; // filter lines
|
|
fT_TCN_P_ = fT_TCN_P;
|
|
fThre_r_ = fThre_r;
|
|
|
|
fMaxCenterDistance2_ = fMaxCenterDistance_ * fMaxCenterDistance_;
|
|
}
|
|
|
|
void EllipseDetector::SetMCD(float fMaxCenterDistance)
|
|
{
|
|
fMaxCenterDistance_ = fMaxCenterDistance;
|
|
fMaxCenterDistance2_ = fMaxCenterDistance_ * fMaxCenterDistance_;
|
|
}
|
|
|
|
void EllipseDetector::RemoveStraightLine(VVP& segments, VVP& segments_update, int id)
|
|
{
|
|
int countedges = 0;
|
|
// For each edge
|
|
for (int i = 0; i < segments.size(); ++i)
|
|
{
|
|
VP& edgeSegment = segments[i];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_OBOX
|
|
// Selection strategy - Step 1 - See Sect [3.1.2] of the paper
|
|
// Constraint on axes aspect ratio
|
|
RotatedRect oriented = minAreaRect(edgeSegment);
|
|
float o_min = min(oriented.size.width, oriented.size.height);
|
|
|
|
if (o_min < fMinOrientedRectSide_)
|
|
{
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// Order edge points of the same arc
|
|
if (id == 0 || id == 1 || id == 4 || id == 5) {
|
|
sort(edgeSegment.begin(), edgeSegment.end(), _SortTopLeft2BottomRight);
|
|
}
|
|
else if (id == 2 || id == 3 || id == 6 || id == 7) {
|
|
sort(edgeSegment.begin(), edgeSegment.end(), _SortBottomLeft2TopRight);
|
|
}
|
|
|
|
int iEdgeSegmentSize = int(edgeSegment.size());
|
|
|
|
// Get extrema of the arc
|
|
Point& left = edgeSegment[0];
|
|
Point& right = edgeSegment[iEdgeSegmentSize - 1];
|
|
|
|
#ifndef DISCARD_TCN
|
|
#ifndef DISCARD_TCN2
|
|
int flag = 0;
|
|
for (int j = 0; j<iEdgeSegmentSize; j++) {
|
|
Point& mid = edgeSegment[j];
|
|
float data[] = { left.x, left.y, 1, mid.x, mid.y, 1, right.x, right.y, 1 };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans/edgelength2;
|
|
double TCNl = ans / (2 * sqrt(edgelength2));
|
|
if (abs(TCNl) > fT_TCN_L_) {
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (0 == flag) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifndef DISCARD_TCN1
|
|
Point& mid = edgeSegment[iEdgeSegmentSize / 2];
|
|
float data[] = { float(left.x), float(left.y), 1.0f, float(mid.x), float(mid.y),
|
|
1.0f, float(right.x), float(right.y), 1.0f };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans / edgelength2;
|
|
double TCNl = ans / (2 * pow(edgelength2, fT_TCN_P_));
|
|
if (abs(TCNl) < fT_TCN_L_) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
segments_update.push_back(edgeSegment);
|
|
}
|
|
}
|
|
|
|
void EllipseDetector::Detect(cv::Mat& I, std::vector<Ellipse>& ellipses)
|
|
{
|
|
countOfFindEllipse_ = 0;
|
|
countOfGetFastCenter_ = 0;
|
|
|
|
Mat1b gray;
|
|
cvtColor(I, gray, CV_BGR2GRAY);
|
|
|
|
|
|
// Set the image size
|
|
szIm_ = I.size();
|
|
|
|
// Initialize temporary data structures
|
|
Mat1b DP = Mat1b::zeros(szIm_); // arcs along positive diagonal
|
|
Mat1b DN = Mat1b::zeros(szIm_); // arcs along negative diagonal
|
|
|
|
|
|
ACC_N_SIZE = 101;
|
|
ACC_R_SIZE = 180;
|
|
ACC_A_SIZE = max(szIm_.height, szIm_.width);
|
|
|
|
// Allocate accumulators
|
|
accN = new int[ACC_N_SIZE];
|
|
accR = new int[ACC_R_SIZE];
|
|
accA = new int[ACC_A_SIZE];
|
|
|
|
|
|
// Other temporary
|
|
|
|
unordered_map<uint, EllipseData> centers; // hash map for reusing already computed EllipseData
|
|
|
|
PreProcessing(gray, DP, DN);
|
|
|
|
|
|
points_1.clear();
|
|
points_2.clear();
|
|
points_3.clear();
|
|
points_4.clear();
|
|
// Detect edges and find convexities
|
|
DetectEdges13(DP, points_1, points_3);
|
|
DetectEdges24(DN, points_2, points_4);
|
|
|
|
|
|
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);
|
|
|
|
sort(ellipses.begin(), ellipses.end());
|
|
|
|
|
|
// Free accumulator memory
|
|
delete[] accN;
|
|
delete[] accR;
|
|
delete[] accA;
|
|
|
|
// Cluster detections
|
|
ClusterEllipses(ellipses);
|
|
}
|
|
|
|
void EllipseDetector::Detect(Mat3b& I, vector<Ellipse>& ellipses)
|
|
{
|
|
countOfFindEllipse_ = 0;
|
|
countOfGetFastCenter_ = 0;
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Tic(0); // prepare data structure
|
|
#endif
|
|
|
|
// Convert to grayscale
|
|
Mat1b gray;
|
|
cvtColor(I, gray, CV_BGR2GRAY);
|
|
|
|
// Set the image size
|
|
szIm_ = I.size();
|
|
|
|
// Initialize temporary data structures
|
|
Mat1b DP = Mat1b::zeros(szIm_); // arcs along positive diagonal
|
|
Mat1b DN = Mat1b::zeros(szIm_); // arcs along negative diagonal
|
|
|
|
// Initialize accumulator dimensions
|
|
ACC_N_SIZE = 101;
|
|
ACC_R_SIZE = 180;
|
|
ACC_A_SIZE = max(szIm_.height, szIm_.width);
|
|
|
|
// Allocate accumulators
|
|
accN = new int[ACC_N_SIZE];
|
|
accR = new int[ACC_R_SIZE];
|
|
accA = new int[ACC_A_SIZE];
|
|
|
|
// Other temporary
|
|
|
|
unordered_map<uint, EllipseData> centers; // hash map for reusing already computed EllipseData
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Toc(0, "prepare data structure"); // prepare data structure
|
|
#endif
|
|
|
|
// Preprocessing
|
|
// From input image I, find edge point with coarse convexity along positive (DP) or negative (DN) diagonal
|
|
PreProcessing(gray, DP, DN);
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Tic(3); // preprocessing
|
|
#endif
|
|
|
|
points_1.clear();
|
|
points_2.clear();
|
|
points_3.clear();
|
|
points_4.clear();
|
|
// Detect edges and find convexities
|
|
DetectEdges13(DP, points_1, points_3);
|
|
DetectEdges24(DN, points_2, points_4);
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Toc(3, "preprocessing_2"); // preprocessing
|
|
#endif
|
|
|
|
// #define DEBUG_PREPROCESSING_S4
|
|
#ifdef DEBUG_PREPROCESSING_S4
|
|
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;
|
|
}
|
|
cv::imshow("PreProcessing->Output", out);
|
|
waitKey();
|
|
#endif
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Tic(4); // grouping
|
|
#endif
|
|
|
|
|
|
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);
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Toc(4, "grouping"); // grouping
|
|
#endif
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Tic(5);
|
|
#endif
|
|
|
|
// Sort detected ellipses with respect to score
|
|
sort(ellipses.begin(), ellipses.end());
|
|
|
|
// Free accumulator memory
|
|
delete[] accN;
|
|
delete[] accR;
|
|
delete[] accA;
|
|
|
|
// Cluster detections
|
|
ClusterEllipses(ellipses);
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Toc(5, "cluster detections");
|
|
#endif
|
|
}
|
|
|
|
void EllipseDetector::PreProcessing(Mat1b& I, Mat1b& arcs8)
|
|
{
|
|
GaussianBlur(I, I, szPreProcessingGaussKernel_, dPreProcessingGaussSigma_);
|
|
|
|
// Temp variables
|
|
Mat1b E; // edge mask
|
|
Mat1s DX, DY; // sobel derivatives
|
|
|
|
_tag_canny(I, E, DX, DY, 3, false, dPercentNe_); // Detect edges
|
|
|
|
// Mat1f dxf, dyf;
|
|
// normalize(DX, dxf, 0, 1, NORM_MINMAX);
|
|
// normalize(DY, dyf, 0, 1, NORM_MINMAX);
|
|
//Mat1f dx, dy;
|
|
//DX.convertTo(dx, CV_32F);
|
|
//DY.convertTo(dy, CV_32F);
|
|
//// DYDX_ = dy / dx;
|
|
|
|
////Mat1f edx, edy;
|
|
////Sobel(E, edx, CV_32F, 1, 0);
|
|
////Sobel(E, edy, CV_32F, 0, 1);
|
|
|
|
//DYDX_ = -1 / (dy / dx);
|
|
//CV_Assert(DYDX_.type() == CV_32F);
|
|
|
|
//cv::GaussianBlur(dx, dx, Size(5, 5), 0, 0);
|
|
//cv::GaussianBlur(dy, dy, Size(5, 5), 0, 0);
|
|
//cv::phase(dx, dy, EO_);
|
|
|
|
#define SIGN(n) ((n)<=0?((n)<0?-1:0):1)
|
|
|
|
// cout << SIGN(0) << " " << SIGN(-1) << " " << SIGN(1) << endl;
|
|
|
|
int sign_x, sign_y, sign_xy;
|
|
for (int i = 0; i < szIm_.height; ++i) {
|
|
short* _dx = DX.ptr<short>(i);
|
|
short* _dy = DY.ptr<short>(i);
|
|
uchar* _e = E.ptr<uchar>(i);
|
|
uchar* _arc = arcs8.ptr<uchar>(i);
|
|
|
|
for (int j = 0; j < szIm_.width; ++j) {
|
|
if (!(_e[j] <= 0)) // !!!!!!
|
|
{
|
|
sign_x = SIGN(_dx[j]);
|
|
sign_y = SIGN(_dy[j]);
|
|
sign_xy = SIGN(abs(_dx[j]) - abs(_dy[j]));
|
|
if (sign_x == 1 && sign_y == 1 && sign_xy == 1) {
|
|
_arc[j] = (uchar)3;
|
|
}
|
|
else if (sign_x == 1 && sign_y == 1 && sign_xy == -1) {
|
|
_arc[j] = (uchar)4;
|
|
}
|
|
else if (sign_x == 1 && sign_y == -1 && sign_xy == -1) {
|
|
_arc[j] = (uchar)1;
|
|
}
|
|
else if (sign_x == 1 && sign_y == -1 && sign_xy == 1) {
|
|
_arc[j] = (uchar)2;
|
|
}
|
|
else if (sign_x == -1 && sign_y == -1 && sign_xy == 1) {
|
|
_arc[j] = (uchar)7;
|
|
}
|
|
else if (sign_x == -1 && sign_y == -1 && sign_xy == -1) {
|
|
_arc[j] = (uchar)8;
|
|
}
|
|
else if (sign_x == -1 && sign_y == 1 && sign_xy == -1) {
|
|
_arc[j] = (uchar)5;
|
|
}
|
|
else if (sign_x == -1 && sign_y == 1 && sign_xy == 1) {
|
|
_arc[j] = (uchar)6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EllipseDetector::PreProcessing(Mat1b& I, Mat1b& DP, Mat1b& DN)
|
|
{
|
|
#ifdef DEBUG_SPEED
|
|
Tic(1); // edge detection
|
|
#endif
|
|
|
|
GaussianBlur(I, I, szPreProcessingGaussKernel_, dPreProcessingGaussSigma_);
|
|
|
|
// Temp variables
|
|
Mat1b E; // edge mask
|
|
Mat1s DX, DY; // sobel derivatives
|
|
|
|
// Detect edges
|
|
_tag_canny(I, E, DX, DY, 3, false, dPercentNe_);
|
|
|
|
Mat1f dx, dy;
|
|
DX.convertTo(dx, CV_32F);
|
|
DY.convertTo(dy, CV_32F);
|
|
//// cv::GaussianBlur(dx, dx, Size(5, 5), 0, 0);
|
|
//// cv::GaussianBlur(dy, dy, Size(5, 5), 0, 0);
|
|
cv::phase(dx, dy, EO_);
|
|
|
|
#ifdef DEBUG_PREPROCESSING
|
|
imshow("PreProcessing->Edge", E); waitKey(50);
|
|
#endif
|
|
|
|
#ifdef DEBUG_SPEED
|
|
Toc(1, "edge detection"); // edge detection
|
|
Tic(2); // preprocessing
|
|
#endif
|
|
|
|
float M_3_2_PI = M_PI + M_1_2_PI;
|
|
// For each edge points, compute the edge direction
|
|
for (int i = 0; i < szIm_.height; ++i) {
|
|
float* _o = EO_.ptr<float>(i);
|
|
uchar* _e = E.ptr<uchar>(i);
|
|
uchar* _dp = DP.ptr<uchar>(i);
|
|
uchar* _dn = DN.ptr<uchar>(i);
|
|
|
|
for (int j = 0; j < szIm_.width; ++j) {
|
|
if (!(_e[j] <= 0)) // !!!!!!
|
|
{
|
|
if (_o[j] == 0 || _o[j] == M_1_2_PI || _o[j] == M_PI || _o[j] == M_3_2_PI) {
|
|
_dn[j] = (uchar)255; _dp[j] = (uchar)255;
|
|
}
|
|
else if ((_o[j] > 0 && _o[j] < M_1_2_PI) || (_o[j] > M_PI && _o[j] < M_3_2_PI)) _dn[j] = (uchar)255;
|
|
else _dp[j] = (uchar)255;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_PREPROCESSING
|
|
imshow("PreProcessing->DP", DP); waitKey(50);
|
|
imshow("PreProcessing->DN", DN); waitKey();
|
|
#endif
|
|
#ifdef DEBUG_SPEED
|
|
Toc(2, "preprocessing"); // preprocessing
|
|
#endif
|
|
}
|
|
|
|
void EllipseDetector::DetectEdges13(Mat1b& DP, VVP& points_1, VVP& points_3)
|
|
{
|
|
// Vector of connected edge points
|
|
VVP contours;
|
|
int countedges = 0;
|
|
// Labeling 8-connected edge points, discarding edge too small
|
|
_tag_find_contours(DP, contours, iMinEdgeLength_); // put small arc edges to a vector
|
|
|
|
#ifdef DEBUG_PREPROCESSING
|
|
Mat1b DP_show = DP.clone();
|
|
_tag_show_contours(DP_show, contours, "PreProcessing->Contours13"); waitKey();
|
|
#endif
|
|
|
|
|
|
// For each edge
|
|
for (int i = 0; i < contours.size(); ++i)
|
|
{
|
|
VP& edgeSegment = contours[i];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_OBOX
|
|
// Selection strategy - Step 1 - See Sect [3.1.2] of the paper
|
|
// Constraint on axes aspect ratio
|
|
RotatedRect oriented = minAreaRect(edgeSegment);
|
|
float o_min = min(oriented.size.width, oriented.size.height);
|
|
|
|
if (o_min < fMinOrientedRectSide_)
|
|
{
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// Order edge points of the same arc
|
|
sort(edgeSegment.begin(), edgeSegment.end(), _SortTopLeft2BottomRight);
|
|
int iEdgeSegmentSize = int(edgeSegment.size());
|
|
|
|
// Get extrema of the arc
|
|
Point& left = edgeSegment[0];
|
|
Point& right = edgeSegment[iEdgeSegmentSize - 1];
|
|
|
|
#ifndef DISCARD_TCN
|
|
#ifndef DISCARD_TCN2
|
|
int flag = 0;
|
|
for (int j = 0; j<iEdgeSegmentSize; j++) {
|
|
Point& mid = edgeSegment[j];
|
|
float data[] = { left.x, left.y, 1, mid.x, mid.y, 1, right.x, right.y, 1 };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans/edgelength2;
|
|
double TCNl = ans / (2 * sqrt(edgelength2));
|
|
if (abs(TCNl) > fT_TCN_L_) {
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (0 == flag) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifndef DISCARD_TCN1
|
|
Point& mid = edgeSegment[iEdgeSegmentSize / 2];
|
|
float data[] = { float(left.x), float(left.y), 1.0f, float(mid.x), float(mid.y),
|
|
1.0f, float(right.x), float(right.y), 1.0f };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans / edgelength2;
|
|
double TCNl = ans / (2 * pow(edgelength2, fT_TCN_P_));
|
|
if (abs(TCNl) < fT_TCN_L_) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Find convexity - See Sect [3.1.3] of the paper
|
|
int iCountTop = 0;
|
|
int xx = left.x;
|
|
for (int k = 1; k < iEdgeSegmentSize; ++k)
|
|
{
|
|
if (edgeSegment[k].x == xx) continue;
|
|
|
|
iCountTop += (edgeSegment[k].y - left.y);
|
|
xx = edgeSegment[k].x;
|
|
}
|
|
|
|
int width = abs(right.x - left.x) + 1;
|
|
int height = abs(right.y - left.y) + 1;
|
|
int iCountBottom = (width * height) - iEdgeSegmentSize - iCountTop;
|
|
|
|
if (iCountBottom > iCountTop)
|
|
{ // 1
|
|
points_1.push_back(edgeSegment);
|
|
}
|
|
else if (iCountBottom < iCountTop)
|
|
{ // 3
|
|
points_3.push_back(edgeSegment);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void EllipseDetector::DetectEdges24(Mat1b& DN, VVP& points_2, VVP& points_4)
|
|
{
|
|
// Vector of connected edge points
|
|
VVP contours;
|
|
int countedges = 0;
|
|
/// Labeling 8-connected edge points, discarding edge too small
|
|
_tag_find_contours(DN, contours, iMinEdgeLength_);
|
|
|
|
#ifdef DEBUG_PREPROCESSING
|
|
_tag_show_contours(DN, contours, "PreProcessing->Contours24"); waitKey();
|
|
#endif
|
|
|
|
int iContoursSize = unsigned(contours.size());
|
|
|
|
|
|
// For each edge
|
|
for (int i = 0; i < iContoursSize; ++i)
|
|
{
|
|
VP& edgeSegment = contours[i];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_OBOX
|
|
// Selection strategy - Step 1 - See Sect [3.1.2] of the paper
|
|
// Constraint on axes aspect ratio
|
|
RotatedRect oriented = minAreaRect(edgeSegment);
|
|
float o_min = min(oriented.size.width, oriented.size.height);
|
|
|
|
if (o_min < fMinOrientedRectSide_)
|
|
{
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// Order edge points of the same arc
|
|
sort(edgeSegment.begin(), edgeSegment.end(), _SortBottomLeft2TopRight);
|
|
int iEdgeSegmentSize = unsigned(edgeSegment.size());
|
|
|
|
// Get extrema of the arc
|
|
Point& left = edgeSegment[0];
|
|
Point& right = edgeSegment[iEdgeSegmentSize - 1];
|
|
|
|
#ifndef DISCARD_TCN
|
|
#ifndef DISCARD_TCN2
|
|
int flag = 0;
|
|
for (int j = 0; j<iEdgeSegmentSize; j++) {
|
|
Point& mid = edgeSegment[j];
|
|
float data[] = { left.x, left.y, 1, mid.x, mid.y, 1, right.x, right.y, 1 };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans/edgelength2;
|
|
double TCNl = ans / (2 * sqrt(edgelength2));
|
|
if (abs(TCNl) > fT_TCN_L_) {
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (0 == flag) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifndef DISCARD_TCN1
|
|
Point& mid = edgeSegment[iEdgeSegmentSize / 2];
|
|
float data[] = { float(left.x), float(left.y), 1.0f, float(mid.x), float(mid.y),
|
|
1.0f, float(right.x), float(right.y), 1.0f };
|
|
Mat threePoints(Size(3, 3), CV_32FC1, data);
|
|
double ans = determinant(threePoints);
|
|
|
|
float dx = 1.0f*(left.x - right.x);
|
|
float dy = 1.0f*(left.y - right.y);
|
|
float edgelength2 = dx*dx + dy*dy;
|
|
// double TCNl = ans / edgelength2;
|
|
double TCNl = ans / (2 * pow(edgelength2, fT_TCN_P_));
|
|
if (abs(TCNl) < fT_TCN_L_) {
|
|
countedges++;
|
|
continue;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Find convexity - See Sect [3.1.3] of the paper
|
|
int iCountBottom = 0;
|
|
int xx = left.x;
|
|
for (int k = 1; k < iEdgeSegmentSize; ++k)
|
|
{
|
|
if (edgeSegment[k].x == xx) continue;
|
|
|
|
iCountBottom += (left.y - edgeSegment[k].y);
|
|
xx = edgeSegment[k].x;
|
|
}
|
|
|
|
int width = abs(right.x - left.x) + 1;
|
|
int height = abs(right.y - left.y) + 1;
|
|
int iCountTop = (width *height) - iEdgeSegmentSize - iCountBottom;
|
|
|
|
if (iCountBottom > iCountTop)
|
|
{
|
|
// 2
|
|
points_2.push_back(edgeSegment);
|
|
}
|
|
else if (iCountBottom < iCountTop)
|
|
{
|
|
// 4
|
|
points_4.push_back(edgeSegment);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
float inline ed2(const cv::Point& A, const cv::Point& B)
|
|
{
|
|
return float(((B.x - A.x)*(B.x - A.x) + (B.y - A.y)*(B.y - A.y)));
|
|
}
|
|
|
|
|
|
#define T124 pjf,pjm,pjl,pif,pim,pil // origin
|
|
#define T231 pil,pim,pif,pjf,pjm,pjl
|
|
#define T342 pif,pim,pil,pjf,pjm,pjl
|
|
#define T413 pif,pim,pil,pjl,pjm,pjf
|
|
|
|
|
|
// Verify triplets of arcs with convexity: i=1, j=2, k=4
|
|
void EllipseDetector::Triplets124(VVP& pi,
|
|
VVP& pj,
|
|
VVP& pk,
|
|
unordered_map<uint, EllipseData>& data,
|
|
vector<Ellipse>& ellipses
|
|
)
|
|
{
|
|
// get arcs length
|
|
ushort sz_i = ushort(pi.size());
|
|
ushort sz_j = ushort(pj.size());
|
|
ushort sz_k = ushort(pk.size());
|
|
|
|
// For each edge i
|
|
for (ushort i = 0; i < sz_i; ++i)
|
|
{
|
|
VP& edge_i = pi[i];
|
|
ushort sz_ei = ushort(edge_i.size());
|
|
|
|
Point& pif = edge_i[0];
|
|
Point& pim = edge_i[sz_ei / 2];
|
|
Point& pil = edge_i[sz_ei - 1];
|
|
|
|
// 1,2 -> reverse 1, swap
|
|
VP rev_i(edge_i.size());
|
|
reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin());
|
|
|
|
// For each edge j
|
|
for (ushort j = 0; j < sz_j; ++j)
|
|
{
|
|
vector<Ellipse> ellipses_i;
|
|
|
|
VP& edge_j = pj[j];
|
|
ushort sz_ej = ushort(edge_j.size());
|
|
|
|
Point& pjf = edge_j[0];
|
|
Point& pjm = edge_j[sz_ej / 2];
|
|
Point& pjl = edge_j[sz_ej - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
|
|
//if (sqrt((pjl.x - pif.x)*(pjl.x - pif.x) + (pjl.y - pif.y)*(pjl.y - pif.y)) > MAX(edge_i.size(), edge_j.size()))
|
|
// continue;
|
|
double tm1 = _tic();
|
|
// CONSTRAINTS on position
|
|
if (pjl.x > pif.x + fThrArcPosition_) //is right
|
|
continue;
|
|
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
if (_ed2(pil, pjf) / _ed2(pif, pjl) < fThre_r_)
|
|
continue;
|
|
|
|
#ifdef CONSTRAINT_CNC_1
|
|
tm1 = _tic();
|
|
// cnc constraint1 2se se1//pil,pim,pif,pjf,pjm,pjl pjf,pjm,pjl,pif,pim,pil
|
|
if (fabs(_value4SixPoints(T124) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
EllipseData data_ij;
|
|
uint key_ij = GenerateKey(PAIR_12, i, j);
|
|
// If the data for the pair i-j have not been computed yet
|
|
if (data.count(key_ij) == 0)
|
|
{
|
|
// 1,2 -> reverse 1, swap
|
|
// Compute data!
|
|
GetFastCenter(edge_j, rev_i, data_ij);
|
|
// Insert computed data in the hash table
|
|
data.insert(pair<uint, EllipseData>(key_ij, data_ij));
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, just lookup the data in the hash table
|
|
data_ij = data.at(key_ij);
|
|
}
|
|
|
|
// for each edge k
|
|
for (ushort k = 0; k < sz_k; ++k)
|
|
{
|
|
VP& edge_k = pk[k];
|
|
ushort sz_ek = ushort(edge_k.size());
|
|
|
|
Point& pkf = edge_k[0];
|
|
Point& pkm = edge_k[sz_ek / 2];
|
|
Point& pkl = edge_k[sz_ek - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
// CONSTRAINTS on position
|
|
if (pkl.y < pil.y - fThrArcPosition_)
|
|
continue;
|
|
#endif
|
|
#ifdef CONSTRAINT_CNC_2
|
|
// cnc constraint2
|
|
if (fabs(_value4SixPoints(pif, pim, pil, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
#ifdef CONSTRAINT_CNC_3
|
|
// cnc constraint3
|
|
if (fabs(_value4SixPoints(pjf, pjm, pjl, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
|
|
uint key_ik = GenerateKey(PAIR_14, i, k);
|
|
|
|
// Find centers
|
|
EllipseData data_ik;
|
|
|
|
// If the data for the pair i-k have not been computed yet
|
|
if (data.count(key_ik) == 0)
|
|
{
|
|
// 1,4 -> ok
|
|
// Compute data!
|
|
GetFastCenter(edge_i, edge_k, data_ik);
|
|
// Insert computed data in the hash table
|
|
data.insert(pair<uint, EllipseData>(key_ik, data_ik));
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, just lookup the data in the hash table
|
|
data_ik = data.at(key_ik);
|
|
}
|
|
|
|
// INVALID CENTERS
|
|
if (!data_ij.isValid || !data_ik.isValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifndef DISCARD_CONSTRAINT_CENTER
|
|
// Selection strategy - Step 3. See Sect [3.2.2] in the paper
|
|
// The computed centers are not close enough
|
|
if (ed2(data_ij.Cab, data_ik.Cab) > fMaxCenterDistance2_)
|
|
{
|
|
// discard
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// If all constraints of the selection strategy have been satisfied,
|
|
// we can start estimating the ellipse parameters
|
|
|
|
// Find ellipse parameters
|
|
// Get the coordinates of the center (xc, yc)
|
|
Point2f center = GetCenterCoordinates(data_ij, data_ik);
|
|
|
|
Ellipse ell;
|
|
// Find remaining paramters (A,B,rho)
|
|
FindEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ell);
|
|
ellipses_i.push_back(ell);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
int rt = -1;
|
|
float rs = 0;
|
|
for (int t = 0; t < (int)ellipses_i.size(); t++)
|
|
{
|
|
if (ellipses_i[t].score_ > rs)
|
|
{
|
|
rs = ellipses_i[t].score_; rt = t;
|
|
}
|
|
}
|
|
if (rt > -1)
|
|
{
|
|
ellipses.push_back(ellipses_i[rt]);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
}
|
|
}
|
|
}
|
|
|
|
void EllipseDetector::Triplets231(VVP& pi,
|
|
VVP& pj,
|
|
VVP& pk,
|
|
unordered_map<uint, EllipseData>& data,
|
|
vector<Ellipse>& ellipses
|
|
)
|
|
{
|
|
ushort sz_i = ushort(pi.size());
|
|
ushort sz_j = ushort(pj.size());
|
|
ushort sz_k = ushort(pk.size());
|
|
|
|
// For each edge i
|
|
for (ushort i = 0; i < sz_i; ++i)
|
|
{
|
|
VP& edge_i = pi[i];
|
|
ushort sz_ei = ushort(edge_i.size());
|
|
|
|
Point& pif = edge_i[0];
|
|
Point& pim = edge_i[sz_ei / 2];
|
|
Point& pil = edge_i[sz_ei - 1];
|
|
|
|
VP rev_i(edge_i.size());
|
|
reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin());
|
|
|
|
// For each edge j
|
|
for (ushort j = 0; j < sz_j; ++j)
|
|
{
|
|
vector<Ellipse> ellipses_i;
|
|
|
|
VP& edge_j = pj[j];
|
|
ushort sz_ej = ushort(edge_j.size());
|
|
|
|
Point& pjf = edge_j[0];
|
|
Point& pjm = edge_j[sz_ej / 2];
|
|
Point& pjl = edge_j[sz_ej - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
//if (sqrt((pjf.x - pif.x)*(pjf.x - pif.x) + (pjf.y - pif.y)*(pjf.y - pif.y)) > MAX(edge_i.size(), edge_j.size()))
|
|
// continue;
|
|
|
|
double tm1 = _tic();
|
|
// CONSTRAINTS on position
|
|
if (pjf.y < pif.y - fThrArcPosition_)
|
|
continue;
|
|
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
if (_ed2(pif, pjf) / _ed2(pil, pjl) < fThre_r_)
|
|
continue;
|
|
|
|
#ifdef CONSTRAINT_CNC_1
|
|
tm1 = _tic();
|
|
// cnc constraint1 2es se3 // pif,pim,pil,pjf,pjm,pjl pil,pim,pif,pjf,pjm,pjl
|
|
if (fabs(_value4SixPoints(T231) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
VP rev_j(edge_j.size());
|
|
reverse_copy(edge_j.begin(), edge_j.end(), rev_j.begin());
|
|
|
|
EllipseData data_ij;
|
|
uint key_ij = GenerateKey(PAIR_23, i, j);
|
|
if (data.count(key_ij) == 0)
|
|
{
|
|
// 2,3 -> reverse 2,3
|
|
GetFastCenter(rev_i, rev_j, data_ij);
|
|
data.insert(pair<uint, EllipseData>(key_ij, data_ij));
|
|
}
|
|
else
|
|
{
|
|
data_ij = data.at(key_ij);
|
|
}
|
|
|
|
// For each edge k
|
|
for (ushort k = 0; k < sz_k; ++k)
|
|
{
|
|
VP& edge_k = pk[k];
|
|
|
|
ushort sz_ek = ushort(edge_k.size());
|
|
|
|
Point& pkf = edge_k[0];
|
|
Point& pkm = edge_k[sz_ek / 2];
|
|
Point& pkl = edge_k[sz_ek - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
// CONSTRAINTS on position
|
|
if (pkf.x < pil.x - fThrArcPosition_)
|
|
continue;
|
|
#endif
|
|
|
|
#ifdef CONSTRAINT_CNC_2
|
|
// cnc constraint2
|
|
if (fabs(_value4SixPoints(pif, pim, pil, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
#ifdef CONSTRAINT_CNC_3
|
|
// cnc constraint3
|
|
if (fabs(_value4SixPoints(pjf, pjm, pjl, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
uint key_ik = GenerateKey(PAIR_12, k, i);
|
|
|
|
// Find centers
|
|
|
|
EllipseData data_ik;
|
|
|
|
|
|
if (data.count(key_ik) == 0)
|
|
{
|
|
// 2,1 -> reverse 1
|
|
VP rev_k(edge_k.size());
|
|
reverse_copy(edge_k.begin(), edge_k.end(), rev_k.begin());
|
|
|
|
GetFastCenter(edge_i, rev_k, data_ik);
|
|
data.insert(pair<uint, EllipseData>(key_ik, data_ik));
|
|
}
|
|
else
|
|
{
|
|
data_ik = data.at(key_ik);
|
|
}
|
|
|
|
// INVALID CENTERS
|
|
if (!data_ij.isValid || !data_ik.isValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifndef DISCARD_CONSTRAINT_CENTER
|
|
// CONSTRAINT ON CENTERS
|
|
if (ed2(data_ij.Cab, data_ik.Cab) > fMaxCenterDistance2_)
|
|
{
|
|
// discard
|
|
continue;
|
|
}
|
|
#endif
|
|
// Find ellipse parameters
|
|
Point2f center = GetCenterCoordinates(data_ij, data_ik);
|
|
|
|
Ellipse ell;
|
|
FindEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ell);
|
|
ellipses_i.push_back(ell);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
int rt = -1;
|
|
float rs = 0;
|
|
for (int t = 0; t < (int)ellipses_i.size(); t++)
|
|
{
|
|
if (ellipses_i[t].score_ > rs)
|
|
{
|
|
rs = ellipses_i[t].score_; rt = t;
|
|
}
|
|
}
|
|
if (rt > -1)
|
|
{
|
|
ellipses.push_back(ellipses_i[rt]);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
}
|
|
}
|
|
}
|
|
|
|
void EllipseDetector::Triplets342(VVP& pi,
|
|
VVP& pj,
|
|
VVP& pk,
|
|
unordered_map<uint, EllipseData>& data,
|
|
vector<Ellipse>& ellipses
|
|
)
|
|
{
|
|
ushort sz_i = ushort(pi.size());
|
|
ushort sz_j = ushort(pj.size());
|
|
ushort sz_k = ushort(pk.size());
|
|
|
|
// For each edge i
|
|
for (ushort i = 0; i < sz_i; ++i)
|
|
{
|
|
VP& edge_i = pi[i];
|
|
ushort sz_ei = ushort(edge_i.size());
|
|
|
|
Point& pif = edge_i[0];
|
|
Point& pim = edge_i[sz_ei / 2];
|
|
Point& pil = edge_i[sz_ei - 1];
|
|
|
|
VP rev_i(edge_i.size());
|
|
reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin());
|
|
|
|
// For each edge j
|
|
for (ushort j = 0; j < sz_j; ++j)
|
|
{
|
|
vector<Ellipse> ellipses_i;
|
|
|
|
VP& edge_j = pj[j];
|
|
ushort sz_ej = ushort(edge_j.size());
|
|
|
|
Point& pjf = edge_j[0];
|
|
Point& pjm = edge_j[sz_ej / 2];
|
|
Point& pjl = edge_j[sz_ej - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
//if (sqrt((pjf.x - pil.x)*(pjf.x - pil.x) + (pjf.y - pil.y)*(pjf.y - pil.y)) > MAX(edge_i.size(), edge_j.size()))
|
|
// continue;
|
|
|
|
double tm1 = _tic();
|
|
// CONSTRAINTS on position
|
|
if (pjf.x < pil.x - fThrArcPosition_) // is left
|
|
continue;
|
|
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
if (_ed2(pil, pjf) / _ed2(pif, pjl) < fThre_r_)
|
|
continue;
|
|
|
|
#ifdef CONSTRAINT_CNC_1
|
|
tm1 = _tic();
|
|
// cnc constraint1 3se se4 // pil,pim,pif,pjf,pjm,pjl pif,pim,pil,pjf,pjm,pjl
|
|
if (fabs(_value4SixPoints(T342) - 1) > fT_CNC_)
|
|
continue;
|
|
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
VP rev_j(edge_j.size());
|
|
reverse_copy(edge_j.begin(), edge_j.end(), rev_j.begin());
|
|
|
|
EllipseData data_ij;
|
|
uint key_ij = GenerateKey(PAIR_34, i, j);
|
|
|
|
if (data.count(key_ij) == 0)
|
|
{
|
|
// 3,4 -> reverse 4
|
|
|
|
GetFastCenter(edge_i, rev_j, data_ij);
|
|
data.insert(pair<uint, EllipseData>(key_ij, data_ij));
|
|
}
|
|
else
|
|
{
|
|
data_ij = data.at(key_ij);
|
|
}
|
|
|
|
|
|
// For each edge k
|
|
for (ushort k = 0; k < sz_k; ++k)
|
|
{
|
|
VP& edge_k = pk[k];
|
|
ushort sz_ek = ushort(edge_k.size());
|
|
|
|
Point& pkf = edge_k[0];
|
|
Point& pkm = edge_k[sz_ek / 2];
|
|
Point& pkl = edge_k[sz_ek - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
// CONSTRAINTS on position
|
|
if (pkf.y > pif.y + fThrArcPosition_)
|
|
continue;
|
|
#endif
|
|
|
|
#ifdef CONSTRAINT_CNC_2
|
|
// cnc constraint2
|
|
if (fabs(_value4SixPoints(pif, pim, pil, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
#ifdef CONSTRAINT_CNC_3
|
|
// cnc constraint3
|
|
if (fabs(_value4SixPoints(pjf, pjm, pjl, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
uint key_ik = GenerateKey(PAIR_23, k, i);
|
|
|
|
// Find centers
|
|
|
|
EllipseData data_ik;
|
|
|
|
|
|
|
|
if (data.count(key_ik) == 0)
|
|
{
|
|
// 3,2 -> reverse 3,2
|
|
|
|
VP rev_k(edge_k.size());
|
|
reverse_copy(edge_k.begin(), edge_k.end(), rev_k.begin());
|
|
|
|
GetFastCenter(rev_i, rev_k, data_ik);
|
|
|
|
data.insert(pair<uint, EllipseData>(key_ik, data_ik));
|
|
}
|
|
else
|
|
{
|
|
data_ik = data.at(key_ik);
|
|
}
|
|
|
|
|
|
// INVALID CENTERS
|
|
if (!data_ij.isValid || !data_ik.isValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifndef DISCARD_CONSTRAINT_CENTER
|
|
// CONSTRAINT ON CENTERS
|
|
if (ed2(data_ij.Cab, data_ik.Cab) > fMaxCenterDistance2_)
|
|
{
|
|
// discard
|
|
continue;
|
|
}
|
|
#endif
|
|
// Find ellipse parameters
|
|
Point2f center = GetCenterCoordinates(data_ij, data_ik);
|
|
|
|
Ellipse ell;
|
|
FindEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ell);
|
|
ellipses_i.push_back(ell);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
int rt = -1;
|
|
float rs = 0;
|
|
for (int t = 0; t < (int)ellipses_i.size(); t++)
|
|
{
|
|
if (ellipses_i[t].score_ > rs)
|
|
{
|
|
rs = ellipses_i[t].score_; rt = t;
|
|
}
|
|
}
|
|
if (rt > -1)
|
|
{
|
|
ellipses.push_back(ellipses_i[rt]);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
}
|
|
}
|
|
}
|
|
|
|
void EllipseDetector::Triplets413(VVP& pi,
|
|
VVP& pj,
|
|
VVP& pk,
|
|
unordered_map<uint, EllipseData>& data,
|
|
vector<Ellipse>& ellipses
|
|
)
|
|
{
|
|
ushort sz_i = ushort(pi.size());
|
|
ushort sz_j = ushort(pj.size());
|
|
ushort sz_k = ushort(pk.size());
|
|
|
|
// For each edge i
|
|
for (ushort i = 0; i < sz_i; ++i)
|
|
{
|
|
VP& edge_i = pi[i];
|
|
ushort sz_ei = ushort(edge_i.size());
|
|
|
|
Point& pif = edge_i[0];
|
|
Point& pim = edge_i[sz_ei / 2];
|
|
Point& pil = edge_i[sz_ei - 1];
|
|
|
|
VP rev_i(edge_i.size());
|
|
reverse_copy(edge_i.begin(), edge_i.end(), rev_i.begin());
|
|
|
|
// For each edge j
|
|
for (ushort j = 0; j < sz_j; ++j)
|
|
{
|
|
vector<Ellipse> ellipses_i;
|
|
|
|
VP& edge_j = pj[j];
|
|
ushort sz_ej = ushort(edge_j.size());
|
|
|
|
Point& pjf = edge_j[0];
|
|
Point& pjm = edge_j[sz_ej / 2];
|
|
Point& pjl = edge_j[sz_ej - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
//if (sqrt((pjl.x - pil.x)*(pjl.x - pil.x) + (pjl.y - pil.y)*(pjl.y - pil.y)) > MAX(edge_i.size(), edge_j.size()))
|
|
// continue;
|
|
|
|
double tm1 = _tic();
|
|
// CONSTRAINTS on position
|
|
if (pjl.y > pil.y + fThrArcPosition_) // is below
|
|
continue;
|
|
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
if (_ed2(pif, pjf) / _ed2(pil, pjl) < fThre_r_)
|
|
continue;
|
|
|
|
#ifdef CONSTRAINT_CNC_1
|
|
tm1 = _tic();
|
|
// cnc constraint1 4se es1 // pif,pim,pil,pjf,pjm,pjl pil,pim,pif,pjl,pjm,pjf pif,pim,pil,pjl,pjm,pjf
|
|
if (fabs(_value4SixPoints(T413) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
|
|
tm1 = _tic();
|
|
EllipseData data_ij;
|
|
uint key_ij = GenerateKey(PAIR_14, j, i);
|
|
|
|
if (data.count(key_ij) == 0)
|
|
{
|
|
// 4,1 -> OK
|
|
GetFastCenter(edge_i, edge_j, data_ij);
|
|
data.insert(pair<uint, EllipseData>(key_ij, data_ij));
|
|
}
|
|
else
|
|
{
|
|
data_ij = data.at(key_ij);
|
|
}
|
|
|
|
// For each edge k
|
|
for (ushort k = 0; k < sz_k; ++k)
|
|
{
|
|
VP& edge_k = pk[k];
|
|
ushort sz_ek = ushort(edge_k.size());
|
|
|
|
Point& pkf = edge_k[0];
|
|
Point& pkm = edge_k[sz_ek / 2];
|
|
Point& pkl = edge_k[sz_ek - 1];
|
|
|
|
#ifndef DISCARD_CONSTRAINT_POSITION
|
|
// CONSTRAINTS on position
|
|
if (pkl.x > pif.x + fThrArcPosition_)
|
|
continue;
|
|
#endif
|
|
|
|
#ifdef CONSTRAINT_CNC_2
|
|
// cnc constraint2
|
|
if (fabs(_value4SixPoints(pif, pim, pil, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
#ifdef CONSTRAINT_CNC_3
|
|
// cnc constraint2
|
|
if (fabs(_value4SixPoints(pjf, pjm, pjl, pkf, pkm, pkl) - 1) > fT_CNC_)
|
|
continue;
|
|
#endif
|
|
uint key_ik = GenerateKey(PAIR_34, k, i);
|
|
|
|
// Find centers
|
|
|
|
EllipseData data_ik;
|
|
|
|
|
|
|
|
if (data.count(key_ik) == 0)
|
|
{
|
|
// 4,3 -> reverse 4
|
|
GetFastCenter(rev_i, edge_k, data_ik);
|
|
data.insert(pair<uint, EllipseData>(key_ik, data_ik));
|
|
}
|
|
else
|
|
{
|
|
data_ik = data.at(key_ik);
|
|
}
|
|
|
|
// INVALID CENTERS
|
|
if (!data_ij.isValid || !data_ik.isValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifndef DISCARD_CONSTRAINT_CENTER
|
|
// CONSTRAIN ON CENTERS
|
|
if (ed2(data_ij.Cab, data_ik.Cab) > fMaxCenterDistance2_)
|
|
{
|
|
// discard
|
|
continue;
|
|
}
|
|
#endif
|
|
// Find ellipse parameters
|
|
Point2f center = GetCenterCoordinates(data_ij, data_ik);
|
|
|
|
Ellipse ell;
|
|
FindEllipses(center, edge_i, edge_j, edge_k, data_ij, data_ik, ell);
|
|
ellipses_i.push_back(ell);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
int rt = -1;
|
|
float rs = 0;
|
|
for (int t = 0; t < (int)ellipses_i.size(); t++)
|
|
{
|
|
if (ellipses_i[t].score_ > rs)
|
|
{
|
|
rs = ellipses_i[t].score_; rt = t;
|
|
}
|
|
}
|
|
if (rt > -1)
|
|
{
|
|
ellipses.push_back(ellipses_i[rt]);
|
|
}
|
|
/*-----------------------------------------------------------------*/
|
|
}
|
|
}
|
|
}
|
|
|
|
int EllipseDetector::FindMaxK(const int* v) const
|
|
{
|
|
int max_val = 0;
|
|
int max_idx = 0;
|
|
for (int i = 0; i<ACC_R_SIZE; ++i)
|
|
{
|
|
(v[i] > max_val) ? max_val = v[i], max_idx = i : (int)NULL;
|
|
}
|
|
|
|
return max_idx + 90;
|
|
}
|
|
|
|
int EllipseDetector::FindMaxN(const int* v) const
|
|
{
|
|
int max_val = 0;
|
|
int max_idx = 0;
|
|
for (int i = 0; i<ACC_N_SIZE; ++i)
|
|
{
|
|
(v[i] > max_val) ? max_val = v[i], max_idx = i : (int)NULL;
|
|
}
|
|
|
|
return max_idx;
|
|
}
|
|
|
|
int EllipseDetector::FindMaxA(const int* v) const
|
|
{
|
|
int max_val = 0;
|
|
int max_idx = 0;
|
|
for (int i = 0; i<ACC_A_SIZE; ++i)
|
|
{
|
|
(v[i] > max_val) ? max_val = v[i], max_idx = i : (int)NULL;
|
|
}
|
|
|
|
return max_idx;
|
|
}
|
|
|
|
// Most important function for detecting ellipses. See Sect[3.2.3] of the paper
|
|
void EllipseDetector::FindEllipses(Point2f& center,
|
|
VP& edge_i, VP& edge_j, VP& edge_k,
|
|
EllipseData& data_ij, EllipseData& data_ik, Ellipse& ell)
|
|
{
|
|
countOfFindEllipse_++;
|
|
// Find ellipse parameters
|
|
|
|
// 0-initialize accumulators
|
|
memset(accN, 0, sizeof(int)*ACC_N_SIZE);
|
|
memset(accR, 0, sizeof(int)*ACC_R_SIZE);
|
|
memset(accA, 0, sizeof(int)*ACC_A_SIZE);
|
|
|
|
// estimation
|
|
// Get size of the 4 vectors of slopes (2 pairs of arcs)
|
|
int sz_ij1 = int(data_ij.Sa.size());
|
|
int sz_ij2 = int(data_ij.Sb.size());
|
|
int sz_ik1 = int(data_ik.Sa.size());
|
|
int sz_ik2 = int(data_ik.Sb.size());
|
|
|
|
// Get the size of the 3 arcs
|
|
size_t sz_ei = edge_i.size();
|
|
size_t sz_ej = edge_j.size();
|
|
size_t sz_ek = edge_k.size();
|
|
|
|
// Center of the estimated ellipse
|
|
float a0 = center.x;
|
|
float b0 = center.y;
|
|
|
|
|
|
// Estimation of remaining parameters
|
|
// Uses 4 combinations of parameters. See Table 1 and Sect [3.2.3] of the paper.
|
|
// ij1 and ik
|
|
{
|
|
float q1 = data_ij.ra;
|
|
float q3 = data_ik.ra;
|
|
float q5 = data_ik.rb;
|
|
|
|
for (int ij1 = 0; ij1 < sz_ij1; ++ij1)
|
|
{
|
|
float q2 = data_ij.Sa[ij1]; // need iter \A3\BF
|
|
|
|
float q1xq2 = q1*q2;
|
|
// ij1 and ik1
|
|
for (int ik1 = 0; ik1 < sz_ik1; ++ik1)
|
|
{
|
|
float q4 = data_ik.Sa[ik1]; // need iter \A3\BF
|
|
|
|
float q3xq4 = q3*q4;
|
|
|
|
// See Eq. [13-18] in the paper
|
|
|
|
float a = (q1xq2 - q3xq4); // gama
|
|
float b = (q3xq4 + 1)*(q1 + q2) - (q1xq2 + 1)*(q3 + q4); // beta
|
|
float Kp = (-b + sqrt(b*b + 4 * a*a)) / (2 * a); // K+
|
|
float zplus = ((q1 - Kp)*(q2 - Kp)) / ((1 + q1*Kp)*(1 + q2*Kp));
|
|
// check zplus and K is linear
|
|
if (zplus >= 0.0f) continue;
|
|
|
|
float Np = sqrt(-zplus); // N+
|
|
float rho = atan(Kp); // rho tmp
|
|
int rhoDeg;
|
|
if (Np > 1.f)
|
|
{
|
|
Np = 1.f / Np;
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; // [0,180)
|
|
}
|
|
else
|
|
{
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; // [0,180) rho angel rep and norm
|
|
}
|
|
|
|
int iNp = cvRound(Np * 100); // [0, 100]
|
|
|
|
if (0 <= iNp && iNp < ACC_N_SIZE &&
|
|
0 <= rhoDeg && rhoDeg < ACC_R_SIZE
|
|
)
|
|
{ // why iter all. beacause zplus and K is not linear?
|
|
++accN[iNp]; // Increment N accumulator
|
|
++accR[rhoDeg]; // Increment R accumulator
|
|
}
|
|
}
|
|
// ij1 and ik2
|
|
for (int ik2 = 0; ik2 < sz_ik2; ++ik2)
|
|
{
|
|
float q4 = data_ik.Sb[ik2];
|
|
|
|
float q5xq4 = q5*q4;
|
|
|
|
// See Eq. [13-18] in the paper
|
|
|
|
float a = (q1xq2 - q5xq4);
|
|
float b = (q5xq4 + 1)*(q1 + q2) - (q1xq2 + 1)*(q5 + q4);
|
|
float Kp = (-b + sqrt(b*b + 4 * a*a)) / (2 * a);
|
|
float zplus = ((q1 - Kp)*(q2 - Kp)) / ((1 + q1*Kp)*(1 + q2*Kp));
|
|
|
|
if (zplus >= 0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float Np = sqrt(-zplus);
|
|
float rho = atan(Kp);
|
|
int rhoDeg;
|
|
if (Np > 1.f)
|
|
{
|
|
Np = 1.f / Np;
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; // [0,180)
|
|
}
|
|
else
|
|
{
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; // [0,180)
|
|
}
|
|
|
|
int iNp = cvRound(Np * 100); // [0, 100]
|
|
|
|
if (0 <= iNp && iNp < ACC_N_SIZE &&
|
|
0 <= rhoDeg && rhoDeg < ACC_R_SIZE
|
|
)
|
|
{
|
|
++accN[iNp]; // Increment N accumulator
|
|
++accR[rhoDeg]; // Increment R accumulator
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// ij2 and ik
|
|
{
|
|
float q1 = data_ij.rb;
|
|
float q3 = data_ik.rb;
|
|
float q5 = data_ik.ra;
|
|
|
|
for (int ij2 = 0; ij2 < sz_ij2; ++ij2)
|
|
{
|
|
float q2 = data_ij.Sb[ij2];
|
|
|
|
float q1xq2 = q1*q2;
|
|
// ij2 and ik2
|
|
for (int ik2 = 0; ik2 < sz_ik2; ++ik2)
|
|
{
|
|
float q4 = data_ik.Sb[ik2];
|
|
|
|
float q3xq4 = q3*q4;
|
|
|
|
// See Eq. [13-18] in the paper
|
|
|
|
float a = (q1xq2 - q3xq4);
|
|
float b = (q3xq4 + 1)*(q1 + q2) - (q1xq2 + 1)*(q3 + q4);
|
|
float Kp = (-b + sqrt(b*b + 4 * a*a)) / (2 * a);
|
|
float zplus = ((q1 - Kp)*(q2 - Kp)) / ((1 + q1*Kp)*(1 + q2*Kp));
|
|
|
|
if (zplus >= 0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float Np = sqrt(-zplus);
|
|
float rho = atan(Kp);
|
|
int rhoDeg;
|
|
if (Np > 1.f)
|
|
{
|
|
Np = 1.f / Np;
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; // [0,180)
|
|
}
|
|
else
|
|
{
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; // [0,180)
|
|
}
|
|
|
|
int iNp = cvRound(Np * 100); // [0, 100]
|
|
|
|
if (0 <= iNp && iNp < ACC_N_SIZE &&
|
|
0 <= rhoDeg && rhoDeg < ACC_R_SIZE
|
|
)
|
|
{
|
|
++accN[iNp]; // Increment N accumulator
|
|
++accR[rhoDeg]; // Increment R accumulator
|
|
}
|
|
}
|
|
|
|
// ij2 and ik1
|
|
for (int ik1 = 0; ik1 < sz_ik1; ++ik1)
|
|
{
|
|
float q4 = data_ik.Sa[ik1];
|
|
|
|
float q5xq4 = q5*q4;
|
|
|
|
// See Eq. [13-18] in the paper
|
|
|
|
float a = (q1xq2 - q5xq4);
|
|
float b = (q5xq4 + 1)*(q1 + q2) - (q1xq2 + 1)*(q5 + q4);
|
|
float Kp = (-b + sqrt(b*b + 4 * a*a)) / (2 * a);
|
|
float zplus = ((q1 - Kp)*(q2 - Kp)) / ((1 + q1*Kp)*(1 + q2*Kp));
|
|
|
|
if (zplus >= 0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float Np = sqrt(-zplus);
|
|
float rho = atan(Kp);
|
|
int rhoDeg;
|
|
if (Np > 1.f)
|
|
{
|
|
Np = 1.f / Np;
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 180) % 180; // [0,180)
|
|
}
|
|
else
|
|
{
|
|
rhoDeg = cvRound((rho * 180 / CV_PI) + 90) % 180; // [0,180)
|
|
}
|
|
|
|
int iNp = cvRound(Np * 100); // [0, 100]
|
|
|
|
if (0 <= iNp && iNp < ACC_N_SIZE &&
|
|
0 <= rhoDeg && rhoDeg < ACC_R_SIZE
|
|
)
|
|
{
|
|
++accN[iNp]; // Increment N accumulator
|
|
++accR[rhoDeg]; // Increment R accumulator
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Find peak in N and K accumulator
|
|
int iN = FindMaxN(accN);
|
|
int iK = FindMaxK(accR);
|
|
|
|
// Recover real values
|
|
float fK = float(iK);
|
|
float Np = float(iN) * 0.01f;
|
|
float rho = fK * float(CV_PI) / 180.f; // deg 2 rad
|
|
float Kp = tan(rho);
|
|
|
|
// Estimate A. See Eq. [19 - 22] in Sect [3.2.3] of the paper
|
|
//
|
|
// may optm
|
|
for (ushort l = 0; l < sz_ei; ++l)
|
|
{
|
|
Point& pp = edge_i[l];
|
|
float sk = 1.f / sqrt(Kp*Kp + 1.f); // cos rho
|
|
float x0 = ((pp.x - a0) * sk) + (((pp.y - b0)*Kp) * sk); // may optm
|
|
float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk); // may optm
|
|
float Ax = sqrt((x0*x0*Np*Np + y0*y0) / ((Np*Np)*(1.f + Kp*Kp)));
|
|
int A = cvRound(abs(Ax / cos(rho))); // may optm
|
|
if ((0 <= A) && (A < ACC_A_SIZE))
|
|
{
|
|
++accA[A];
|
|
}
|
|
}
|
|
|
|
for (ushort l = 0; l < sz_ej; ++l)
|
|
{
|
|
Point& pp = edge_j[l];
|
|
float sk = 1.f / sqrt(Kp*Kp + 1.f);
|
|
float x0 = ((pp.x - a0) * sk) + (((pp.y - b0)*Kp) * sk);
|
|
float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk);
|
|
float Ax = sqrt((x0*x0*Np*Np + y0*y0) / ((Np*Np)*(1.f + Kp*Kp)));
|
|
int A = cvRound(abs(Ax / cos(rho)));
|
|
if ((0 <= A) && (A < ACC_A_SIZE))
|
|
{
|
|
++accA[A];
|
|
}
|
|
}
|
|
|
|
for (ushort l = 0; l < sz_ek; ++l)
|
|
{
|
|
Point& pp = edge_k[l];
|
|
float sk = 1.f / sqrt(Kp*Kp + 1.f);
|
|
float x0 = ((pp.x - a0) * sk) + (((pp.y - b0)*Kp) * sk);
|
|
float y0 = -(((pp.x - a0) * Kp) * sk) + ((pp.y - b0) * sk);
|
|
float Ax = sqrt((x0*x0*Np*Np + y0*y0) / ((Np*Np)*(1.f + Kp*Kp)));
|
|
int A = cvRound(abs(Ax / cos(rho)));
|
|
if ((0 <= A) && (A < ACC_A_SIZE))
|
|
{
|
|
++accA[A];
|
|
}
|
|
}
|
|
|
|
// Find peak in A accumulator
|
|
int A = FindMaxA(accA);
|
|
float fA = float(A);
|
|
|
|
// Find B value. See Eq [23] in the paper
|
|
float fB = abs(fA * Np);
|
|
|
|
// Got all ellipse parameters!
|
|
// Ellipse ell(a0, b0, fA, fB, fmod(rho + float(CV_PI)*2.f, float(CV_PI)));
|
|
ell.xc_ = a0;
|
|
ell.yc_ = b0;
|
|
ell.a_ = fA;
|
|
ell.b_ = fB;
|
|
ell.rad_ = fmod(rho + float(CV_PI)*2.f, float(CV_PI));
|
|
|
|
// estimation end
|
|
// validation start
|
|
// Get the score. See Sect [3.3.1] in the paper
|
|
|
|
// Find the number of edge pixel lying on the ellipse
|
|
float _cos = cos(-ell.rad_);
|
|
float _sin = sin(-ell.rad_);
|
|
|
|
float invA2 = 1.f / (ell.a_ * ell.a_);
|
|
float invB2 = 1.f / (ell.b_ * ell.b_);
|
|
|
|
float invNofPoints = 1.f / float(sz_ei + sz_ej + sz_ek);
|
|
int counter_on_perimeter = 0;
|
|
float probc_on_perimeter = 0;
|
|
|
|
for (ushort l = 0; l < sz_ei; ++l)
|
|
{
|
|
float tx = float(edge_i[l].x) - ell.xc_;
|
|
float ty = float(edge_i[l].y) - ell.yc_;
|
|
float rx = (tx*_cos - ty*_sin);
|
|
float ry = (tx*_sin + ty*_cos);
|
|
|
|
float h = (rx*rx)*invA2 + (ry*ry)*invB2;
|
|
if (abs(h - 1.f) < fDistanceToEllipseContour_)
|
|
{
|
|
++counter_on_perimeter;
|
|
probc_on_perimeter += 1; // (fDistanceToEllipseContour_ - abs(h - 1.f)) / fDistanceToEllipseContour_;
|
|
}
|
|
}
|
|
|
|
for (ushort l = 0; l < sz_ej; ++l)
|
|
{
|
|
float tx = float(edge_j[l].x) - ell.xc_;
|
|
float ty = float(edge_j[l].y) - ell.yc_;
|
|
float rx = (tx*_cos - ty*_sin);
|
|
float ry = (tx*_sin + ty*_cos);
|
|
|
|
float h = (rx*rx)*invA2 + (ry*ry)*invB2;
|
|
if (abs(h - 1.f) < fDistanceToEllipseContour_)
|
|
{
|
|
++counter_on_perimeter;
|
|
probc_on_perimeter += 1; // (fDistanceToEllipseContour_ - abs(h - 1.f)) / fDistanceToEllipseContour_;
|
|
}
|
|
}
|
|
|
|
for (ushort l = 0; l < sz_ek; ++l)
|
|
{
|
|
float tx = float(edge_k[l].x) - ell.xc_;
|
|
float ty = float(edge_k[l].y) - ell.yc_;
|
|
float rx = (tx*_cos - ty*_sin);
|
|
float ry = (tx*_sin + ty*_cos);
|
|
|
|
float h = (rx*rx)*invA2 + (ry*ry)*invB2;
|
|
if (abs(h - 1.f) < fDistanceToEllipseContour_)
|
|
{
|
|
++counter_on_perimeter;
|
|
probc_on_perimeter += 1; // (fDistanceToEllipseContour_ - abs(h - 1.f)) / fDistanceToEllipseContour_;
|
|
}
|
|
}
|
|
|
|
|
|
// no points found on the ellipse
|
|
if (counter_on_perimeter <= 0)
|
|
{
|
|
// validation
|
|
return;
|
|
}
|
|
|
|
// Compute score
|
|
// float score = float(counter_on_perimeter) * invNofPoints;
|
|
float score = probc_on_perimeter * invNofPoints;
|
|
if (score < fMinScore_)
|
|
{
|
|
// validation
|
|
return;
|
|
}
|
|
|
|
// Compute reliability
|
|
// this metric is not described in the paper, mostly due to space limitations.
|
|
// The main idea is that for a given ellipse (TD) even if the score is high, the arcs
|
|
// can cover only a small amount of the contour of the estimated ellipse.
|
|
// A low reliability indicate that the arcs form an elliptic shape by chance, but do not underlie
|
|
// an actual ellipse. The value is normalized between 0 and 1.
|
|
// The default value is 0.4.
|
|
|
|
// It is somehow similar to the "Angular Circumreference Ratio" saliency criteria
|
|
// as in the paper:
|
|
// D. K. Prasad, M. K. Leung, S.-Y. Cho, Edge curvature and convexity
|
|
// based ellipse detection method, Pattern Recognition 45 (2012) 3204-3221.
|
|
|
|
float di, dj, dk;
|
|
{
|
|
Point2f p1(float(edge_i[0].x), float(edge_i[0].y));
|
|
Point2f p2(float(edge_i[sz_ei - 1].x), float(edge_i[sz_ei - 1].y));
|
|
p1.x -= ell.xc_;
|
|
p1.y -= ell.yc_;
|
|
p2.x -= ell.xc_;
|
|
p2.y -= ell.yc_;
|
|
Point2f r1((p1.x*_cos - p1.y*_sin), (p1.x*_sin + p1.y*_cos));
|
|
Point2f r2((p2.x*_cos - p2.y*_sin), (p2.x*_sin + p2.y*_cos));
|
|
di = abs(r2.x - r1.x) + abs(r2.y - r1.y);
|
|
}
|
|
{
|
|
Point2f p1(float(edge_j[0].x), float(edge_j[0].y));
|
|
Point2f p2(float(edge_j[sz_ej - 1].x), float(edge_j[sz_ej - 1].y));
|
|
p1.x -= ell.xc_;
|
|
p1.y -= ell.yc_;
|
|
p2.x -= ell.xc_;
|
|
p2.y -= ell.yc_;
|
|
Point2f r1((p1.x*_cos - p1.y*_sin), (p1.x*_sin + p1.y*_cos));
|
|
Point2f r2((p2.x*_cos - p2.y*_sin), (p2.x*_sin + p2.y*_cos));
|
|
dj = abs(r2.x - r1.x) + abs(r2.y - r1.y);
|
|
}
|
|
{
|
|
Point2f p1(float(edge_k[0].x), float(edge_k[0].y));
|
|
Point2f p2(float(edge_k[sz_ek - 1].x), float(edge_k[sz_ek - 1].y));
|
|
p1.x -= ell.xc_;
|
|
p1.y -= ell.yc_;
|
|
p2.x -= ell.xc_;
|
|
p2.y -= ell.yc_;
|
|
Point2f r1((p1.x*_cos - p1.y*_sin), (p1.x*_sin + p1.y*_cos));
|
|
Point2f r2((p2.x*_cos - p2.y*_sin), (p2.x*_sin + p2.y*_cos));
|
|
dk = abs(r2.x - r1.x) + abs(r2.y - r1.y);
|
|
}
|
|
|
|
// This allows to get rid of thick edges
|
|
float rel = min(1.f, ((di + dj + dk) / (3 * (ell.a_ + ell.b_))));
|
|
|
|
if (rel < fMinReliability_)
|
|
{
|
|
// validation
|
|
return;
|
|
}
|
|
if (_isnan(rel))
|
|
return;
|
|
|
|
// Assign the new score!
|
|
ell.score_ = (score + rel) * 0.5f; // need to change
|
|
// ell.score_ = (score*rel); // need to change
|
|
|
|
if (ell.score_ < fMinScore_)
|
|
{
|
|
return;
|
|
}
|
|
// The tentative detection has been confirmed. Save it!
|
|
// ellipses.push_back(ell);
|
|
|
|
// Validation end
|
|
}
|
|
|
|
|
|
|
|
// Ellipse clustering procedure. See Sect [3.3.2] in the paper.
|
|
void EllipseDetector::ClusterEllipses(vector<Ellipse>& ellipses)
|
|
{
|
|
float th_Da = 0.1f;
|
|
float th_Db = 0.1f;
|
|
float th_Dr = 0.1f;
|
|
|
|
float th_Dc_ratio = 0.1f;
|
|
float th_Dr_circle = 0.9f;
|
|
|
|
int iNumOfEllipses = int(ellipses.size());
|
|
if (iNumOfEllipses == 0) return;
|
|
|
|
// The first ellipse is assigned to a cluster
|
|
vector<Ellipse> clusters;
|
|
clusters.push_back(ellipses[0]);
|
|
|
|
// bool bFoundCluster = false;
|
|
|
|
for (int i = 1; i<iNumOfEllipses; ++i)
|
|
{
|
|
Ellipse& e1 = ellipses[i];
|
|
|
|
int sz_clusters = int(clusters.size());
|
|
|
|
float ba_e1 = e1.b_ / e1.a_;
|
|
// float Decc1 = e1._b / e1._a;
|
|
|
|
bool bFoundCluster = false;
|
|
for (int j = 0; j<sz_clusters; ++j)
|
|
{
|
|
Ellipse& e2 = clusters[j];
|
|
|
|
float ba_e2 = e2.b_ / e2.a_;
|
|
float th_Dc = min(e1.b_, e2.b_) * th_Dc_ratio;
|
|
th_Dc *= th_Dc;
|
|
|
|
// Centers
|
|
float Dc = ((e1.xc_ - e2.xc_)*(e1.xc_ - e2.xc_) + (e1.yc_ - e2.yc_)*(e1.yc_ - e2.yc_));
|
|
if (Dc > th_Dc)
|
|
{
|
|
//not same cluster
|
|
continue;
|
|
}
|
|
|
|
// a
|
|
float Da = abs(e1.a_ - e2.a_) / max(e1.a_, e2.a_);
|
|
if (Da > th_Da)
|
|
{
|
|
//not same cluster
|
|
continue;
|
|
}
|
|
|
|
// b
|
|
float Db = abs(e1.b_ - e2.b_) / min(e1.b_, e2.b_);
|
|
if (Db > th_Db)
|
|
{
|
|
//not same cluster
|
|
continue;
|
|
}
|
|
|
|
// angle
|
|
float Dr = GetMinAnglePI(e1.rad_, e2.rad_) / float(CV_PI);
|
|
if ((Dr > th_Dr) && (ba_e1 < th_Dr_circle) && (ba_e2 < th_Dr_circle))
|
|
{
|
|
//not same cluster
|
|
continue;
|
|
}
|
|
|
|
// Same cluster as e2
|
|
bFoundCluster = true;//
|
|
// Discard, no need to create a new cluster
|
|
break;
|
|
}
|
|
|
|
if (!bFoundCluster)
|
|
{
|
|
// Create a new cluster
|
|
clusters.push_back(e1);
|
|
}
|
|
}
|
|
|
|
clusters.swap(ellipses);
|
|
}
|
|
|
|
float EllipseDetector::GetMedianSlope(vector<Point2f>& med, Point2f& M, vector<float>& slopes)
|
|
{
|
|
// input med slopes, output M, return slope
|
|
// med : vector of points
|
|
// M : centroid of the points in med
|
|
// slopes : vector of the slopes
|
|
|
|
unsigned iNofPoints = unsigned(med.size());
|
|
// CV_Assert(iNofPoints >= 2);
|
|
|
|
unsigned halfSize = iNofPoints >> 1;
|
|
unsigned quarterSize = halfSize >> 1;
|
|
|
|
vector<float> xx, yy;
|
|
slopes.reserve(halfSize);
|
|
xx.reserve(iNofPoints);
|
|
yy.reserve(iNofPoints);
|
|
|
|
for (unsigned i = 0; i < halfSize; ++i)
|
|
{
|
|
Point2f& p1 = med[i];
|
|
Point2f& p2 = med[halfSize + i];
|
|
|
|
xx.push_back(p1.x);
|
|
xx.push_back(p2.x);
|
|
yy.push_back(p1.y);
|
|
yy.push_back(p2.y);
|
|
|
|
float den = (p2.x - p1.x);
|
|
float num = (p2.y - p1.y);
|
|
|
|
if (den == 0) den = 0.00001f;
|
|
|
|
slopes.push_back(num / den);
|
|
}
|
|
|
|
nth_element(slopes.begin(), slopes.begin() + quarterSize, slopes.end());
|
|
nth_element(xx.begin(), xx.begin() + halfSize, xx.end());
|
|
nth_element(yy.begin(), yy.begin() + halfSize, yy.end());
|
|
M.x = xx[halfSize];
|
|
M.y = yy[halfSize];
|
|
|
|
return slopes[quarterSize];
|
|
}
|
|
|
|
int inline sgn(float val) {
|
|
return (0.f < val) - (val < 0.f);
|
|
}
|
|
|
|
void EllipseDetector::GetFastCenter(vector<Point>& e1, vector<Point>& e2, EllipseData& data)
|
|
{
|
|
countOfGetFastCenter_++;
|
|
data.isValid = true;
|
|
|
|
unsigned size_1 = unsigned(e1.size());
|
|
unsigned size_2 = unsigned(e2.size());
|
|
|
|
unsigned hsize_1 = size_1 >> 1;
|
|
unsigned hsize_2 = size_2 >> 1;
|
|
|
|
Point& med1 = e1[hsize_1];
|
|
Point& med2 = e2[hsize_2];
|
|
|
|
Point2f M12, M34;
|
|
float q2, q4;
|
|
|
|
{// First to second Reference slope
|
|
float dx_ref = float(e1[0].x - med2.x);
|
|
float dy_ref = float(e1[0].y - med2.y);
|
|
|
|
if (dy_ref == 0) dy_ref = 0.00001f;
|
|
|
|
float m_ref = dy_ref / dx_ref;
|
|
data.ra = m_ref;
|
|
|
|
// Find points with same slope as reference
|
|
vector<Point2f> med;
|
|
med.reserve(hsize_2);
|
|
|
|
unsigned minPoints = (uNs_ < hsize_2) ? uNs_ : hsize_2; // parallel chords
|
|
|
|
vector<uint> indexes(minPoints);
|
|
if (uNs_ < hsize_2)
|
|
{ // hsize_2 bigger than uNs_
|
|
unsigned iSzBin = hsize_2 / unsigned(uNs_);
|
|
unsigned iIdx = hsize_2 + (iSzBin / 2);
|
|
|
|
for (unsigned i = 0; i<uNs_; ++i)
|
|
{
|
|
indexes[i] = iIdx;
|
|
iIdx += iSzBin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iota(indexes.begin(), indexes.end(), hsize_2); // convert to unsigned
|
|
}
|
|
for (uint ii = 0; ii<minPoints; ++ii)
|
|
{// parallel chords in arc 2
|
|
uint i = indexes[ii];
|
|
|
|
float x1 = float(e2[i].x);
|
|
float y1 = float(e2[i].y);
|
|
|
|
uint begin = 0;
|
|
uint end = size_1 - 1;
|
|
// one point between the first and last point of parallel chords 1
|
|
float xb = float(e1[begin].x);
|
|
float yb = float(e1[begin].y);
|
|
float res_begin = ((xb - x1) * dy_ref) - ((yb - y1) * dx_ref);
|
|
int sign_begin = sgn(res_begin);
|
|
if (sign_begin == 0)
|
|
{
|
|
// found
|
|
med.push_back(Point2f((xb + x1)* 0.5f, (yb + y1)* 0.5f));
|
|
continue;
|
|
}
|
|
|
|
float xe = float(e1[end].x);
|
|
float ye = float(e1[end].y);
|
|
float res_end = ((xe - x1) * dy_ref) - ((ye - y1) * dx_ref);
|
|
int sign_end = sgn(res_end);
|
|
if (sign_end == 0)
|
|
{
|
|
// found
|
|
med.push_back(Point2f((xe + x1)* 0.5f, (ye + y1)* 0.5f));
|
|
continue;
|
|
}
|
|
// if at the same side
|
|
if ((sign_begin + sign_end) != 0)
|
|
{// No parallel chords
|
|
continue;
|
|
}
|
|
|
|
// search parallel chords
|
|
uint j = (begin + end) >> 1;
|
|
while (end - begin > 2)
|
|
{
|
|
float x2 = float(e1[j].x);
|
|
float y2 = float(e1[j].y);
|
|
float res = ((x2 - x1) * dy_ref) - ((y2 - y1) * dx_ref);
|
|
int sign_res = sgn(res);
|
|
|
|
if (sign_res == 0)
|
|
{
|
|
// found
|
|
med.push_back(Point2f((x2 + x1)* 0.5f, (y2 + y1)* 0.5f));
|
|
break;
|
|
}
|
|
|
|
if (sign_res + sign_begin == 0)
|
|
{
|
|
sign_end = sign_res;
|
|
end = j;
|
|
}
|
|
else
|
|
{
|
|
sign_begin = sign_res;
|
|
begin = j;
|
|
}
|
|
j = (begin + end) >> 1;
|
|
}
|
|
// search end error ?
|
|
med.push_back(Point2f((e1[j].x + x1)* 0.5f, (e1[j].y + y1)* 0.5f));
|
|
}
|
|
|
|
if (med.size() < 2)
|
|
{
|
|
data.isValid = false;
|
|
return;
|
|
}
|
|
|
|
/****************************************
|
|
Mat3b out(480, 640, Vec3b(0, 0, 0));
|
|
Vec3b color(0, 255, 0);
|
|
for (int ci = 0; ci < med.size(); ci++)
|
|
circle(out, med[ci], 2, color);
|
|
imshow("test", out); waitKey(100);
|
|
****************************************/
|
|
q2 = GetMedianSlope(med, M12, data.Sa); //get Sa ta = q2 Ma
|
|
}
|
|
|
|
{// Second to first
|
|
// Reference slope
|
|
float dx_ref = float(med1.x - e2[0].x);
|
|
float dy_ref = float(med1.y - e2[0].y);
|
|
|
|
if (dy_ref == 0) dy_ref = 0.00001f;
|
|
|
|
float m_ref = dy_ref / dx_ref;
|
|
data.rb = m_ref;
|
|
|
|
// Find points with same slope as reference
|
|
vector<Point2f> med;
|
|
med.reserve(hsize_1);
|
|
|
|
uint minPoints = (uNs_ < hsize_1) ? uNs_ : hsize_1;
|
|
|
|
vector<uint> indexes(minPoints);
|
|
if (uNs_ < hsize_1)
|
|
{
|
|
unsigned iSzBin = hsize_1 / unsigned(uNs_);
|
|
unsigned iIdx = hsize_1 + (iSzBin / 2);
|
|
|
|
for (unsigned i = 0; i<uNs_; ++i)
|
|
{
|
|
indexes[i] = iIdx;
|
|
iIdx += iSzBin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iota(indexes.begin(), indexes.end(), hsize_1);
|
|
}
|
|
|
|
|
|
for (uint ii = 0; ii<minPoints; ++ii)
|
|
{
|
|
uint i = indexes[ii];
|
|
|
|
float x1 = float(e1[i].x);
|
|
float y1 = float(e1[i].y);
|
|
|
|
uint begin = 0;
|
|
uint end = size_2 - 1;
|
|
|
|
float xb = float(e2[begin].x);
|
|
float yb = float(e2[begin].y);
|
|
float res_begin = ((xb - x1) * dy_ref) - ((yb - y1) * dx_ref);
|
|
int sign_begin = sgn(res_begin);
|
|
if (sign_begin == 0)
|
|
{
|
|
// found
|
|
med.push_back(Point2f((xb + x1)* 0.5f, (yb + y1)* 0.5f));
|
|
continue;
|
|
}
|
|
|
|
float xe = float(e2[end].x);
|
|
float ye = float(e2[end].y);
|
|
float res_end = ((xe - x1) * dy_ref) - ((ye - y1) * dx_ref);
|
|
int sign_end = sgn(res_end);
|
|
if (sign_end == 0)
|
|
{
|
|
// found
|
|
med.push_back(Point2f((xe + x1)* 0.5f, (ye + y1)* 0.5f));
|
|
continue;
|
|
}
|
|
|
|
if ((sign_begin + sign_end) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint j = (begin + end) >> 1;
|
|
|
|
while (end - begin > 2)
|
|
{
|
|
float x2 = float(e2[j].x);
|
|
float y2 = float(e2[j].y);
|
|
float res = ((x2 - x1) * dy_ref) - ((y2 - y1) * dx_ref);
|
|
int sign_res = sgn(res);
|
|
|
|
if (sign_res == 0)
|
|
{
|
|
//found
|
|
med.push_back(Point2f((x2 + x1)* 0.5f, (y2 + y1)* 0.5f));
|
|
break;
|
|
}
|
|
|
|
if (sign_res + sign_begin == 0)
|
|
{
|
|
sign_end = sign_res;
|
|
end = j;
|
|
}
|
|
else
|
|
{
|
|
sign_begin = sign_res;
|
|
begin = j;
|
|
}
|
|
j = (begin + end) >> 1;
|
|
}
|
|
|
|
med.push_back(Point2f((e2[j].x + x1)* 0.5f, (e2[j].y + y1)* 0.5f));
|
|
}
|
|
|
|
if (med.size() < 2)
|
|
{
|
|
data.isValid = false;
|
|
return;
|
|
}
|
|
q4 = GetMedianSlope(med, M34, data.Sb);
|
|
}
|
|
|
|
if (q2 == q4)
|
|
{
|
|
data.isValid = false;
|
|
return;
|
|
}
|
|
|
|
float invDen = 1 / (q2 - q4);
|
|
data.Cab.x = (M34.y - q4*M34.x - M12.y + q2*M12.x) * invDen;
|
|
data.Cab.y = (q2*M34.y - q4*M12.y + q2*q4*(M12.x - M34.x)) * invDen;
|
|
data.ta = q2;
|
|
data.tb = q4;
|
|
data.Ma = M12;
|
|
data.Mb = M34;
|
|
}
|
|
|
|
float EllipseDetector::GetMinAnglePI(float alpha, float beta)
|
|
{
|
|
float pi = float(CV_PI);
|
|
float pi2 = float(2.0 * CV_PI);
|
|
|
|
// normalize data in [0, 2*pi]
|
|
float a = fmod(alpha + pi2, pi2);
|
|
float b = fmod(beta + pi2, pi2);
|
|
|
|
// normalize data in [0, pi]
|
|
if (a > pi)
|
|
a -= pi;
|
|
if (b > pi)
|
|
b -= pi;
|
|
|
|
if (a > b)
|
|
{
|
|
swap(a, b);
|
|
}
|
|
|
|
float diff1 = b - a;
|
|
float diff2 = pi - diff1;
|
|
return min(diff1, diff2);
|
|
}
|
|
|
|
// Get the coordinates of the center, given the intersection of the estimated lines. See Fig. [8] in Sect [3.2.3] in the paper.
|
|
Point2f EllipseDetector::GetCenterCoordinates(EllipseData& data_ij, EllipseData& data_ik)
|
|
{
|
|
float xx[7];
|
|
float yy[7];
|
|
|
|
xx[0] = data_ij.Cab.x;
|
|
xx[1] = data_ik.Cab.x;
|
|
yy[0] = data_ij.Cab.y;
|
|
yy[1] = data_ik.Cab.y;
|
|
|
|
{
|
|
// 1-1
|
|
float q2 = data_ij.ta;
|
|
float q4 = data_ik.ta;
|
|
Point2f& M12 = data_ij.Ma;
|
|
Point2f& M34 = data_ik.Ma;
|
|
|
|
float invDen = 1 / (q2 - q4);
|
|
xx[2] = (M34.y - q4*M34.x - M12.y + q2*M12.x) * invDen;
|
|
yy[2] = (q2*M34.y - q4*M12.y + q2*q4*(M12.x - M34.x)) * invDen;
|
|
}
|
|
|
|
{
|
|
// 1-2
|
|
float q2 = data_ij.ta;
|
|
float q4 = data_ik.tb;
|
|
Point2f& M12 = data_ij.Ma;
|
|
Point2f& M34 = data_ik.Mb;
|
|
|
|
float invDen = 1 / (q2 - q4);
|
|
xx[3] = (M34.y - q4*M34.x - M12.y + q2*M12.x) * invDen;
|
|
yy[3] = (q2*M34.y - q4*M12.y + q2*q4*(M12.x - M34.x)) * invDen;
|
|
}
|
|
|
|
{
|
|
// 2-2
|
|
float q2 = data_ij.tb;
|
|
float q4 = data_ik.tb;
|
|
Point2f& M12 = data_ij.Mb;
|
|
Point2f& M34 = data_ik.Mb;
|
|
|
|
float invDen = 1 / (q2 - q4);
|
|
xx[4] = (M34.y - q4*M34.x - M12.y + q2*M12.x) * invDen;
|
|
yy[4] = (q2*M34.y - q4*M12.y + q2*q4*(M12.x - M34.x)) * invDen;
|
|
}
|
|
|
|
{
|
|
// 2-1
|
|
float q2 = data_ij.tb;
|
|
float q4 = data_ik.ta;
|
|
Point2f& M12 = data_ij.Mb;
|
|
Point2f& M34 = data_ik.Ma;
|
|
|
|
float invDen = 1 / (q2 - q4);
|
|
xx[5] = (M34.y - q4*M34.x - M12.y + q2*M12.x) * invDen;
|
|
yy[5] = (q2*M34.y - q4*M12.y + q2*q4*(M12.x - M34.x)) * invDen;
|
|
}
|
|
|
|
xx[6] = (xx[0] + xx[1]) * 0.5f;
|
|
yy[6] = (yy[0] + yy[1]) * 0.5f;
|
|
|
|
|
|
// Median
|
|
nth_element(xx, xx + 3, xx + 7);
|
|
nth_element(yy, yy + 3, yy + 7);
|
|
float xc = xx[3];
|
|
float yc = yy[3];
|
|
|
|
return Point2f(xc, yc);
|
|
}
|
|
|
|
uint inline EllipseDetector::GenerateKey(uchar pair, ushort u, ushort v)
|
|
{
|
|
return (pair << 30) + (u << 15) + v;
|
|
}
|
|
|
|
// Draw at most iTopN detected ellipses.
|
|
void EllipseDetector::DrawDetectedEllipses(Mat& output, vector<Ellipse>& ellipses, int iTopN, int thickness)
|
|
{
|
|
int sz_ell = int(ellipses.size());
|
|
int n = (iTopN == 0) ? sz_ell : min(iTopN, sz_ell);
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
Ellipse& e = ellipses[n - i - 1];
|
|
int g = cvRound(e.score_ * 255.f);
|
|
Scalar color(0, g, 0);
|
|
ellipse(output, Point(cvRound(e.xc_), cvRound(e.yc_)), Size(cvRound(e.a_), cvRound(e.b_)), e.rad_*180.0 / CV_PI, 0.0, 360.0, color, thickness);
|
|
// cv::circle(output, Point(e.xc_, e.yc_), 2, Scalar(0, 0, 255), 2);
|
|
}
|
|
}
|
|
|
|
bool inline convex_check(VP& vp1, int start, int end)
|
|
{
|
|
int x_min(4096), x_max(0), y_min(4096), y_max(0);
|
|
int integral_u(0), integral_d(0);
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
Point& val = vp1[i];
|
|
x_min = MIN(x_min, val.x);
|
|
x_max = MAX(x_max, val.x);
|
|
y_min = MIN(y_min, val.y);
|
|
y_max = MAX(y_max, val.y);
|
|
}
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
Point& val = vp1[i];
|
|
integral_u += (val.y - y_min);
|
|
integral_d += (y_max - val.y);
|
|
}
|
|
if (integral_u > integral_d)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool inline concave_check(VP& vp1, int start, int end)
|
|
{
|
|
int x_min(4096), x_max(0), y_min(4096), y_max(0);
|
|
int integral_u(0), integral_d(0);
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
Point& val = vp1[i];
|
|
x_min = MIN(x_min, val.x);
|
|
x_max = MAX(x_max, val.x);
|
|
y_min = MIN(y_min, val.y);
|
|
y_max = MAX(y_max, val.y);
|
|
}
|
|
for (int i = start; i <= end; i++)
|
|
{
|
|
Point& val = vp1[i];
|
|
integral_u += (val.y - y_min);
|
|
integral_d += (y_max - val.y);
|
|
}
|
|
if (integral_u < integral_d)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void EllipseDetector::ArcsCheck1234(VVP& points_1, VVP& points_2, VVP& points_3, VVP& points_4)
|
|
{
|
|
static int nchecks = 21;
|
|
int i, j;
|
|
VVP vps_1, vps_2, vps_3, vps_4;
|
|
Mat3b out(480, 640, Vec3b(0, 0, 0));
|
|
for (i = 0; i < points_2.size(); ++i)
|
|
{
|
|
// Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
|
|
Vec3b color(255, 0, 0);
|
|
for (j = 0; j < points_2[i].size(); ++j)
|
|
out(points_2[i][j]) = color;
|
|
}
|
|
imshow("out", out); waitKey();
|
|
|
|
for (i = 0; i < points_1.size(); i++)
|
|
{
|
|
VP& vp1 = points_1[i];
|
|
if (vp1.size() > nchecks)
|
|
{
|
|
VP vpn;
|
|
for (j = 0; j <= vp1.size() - nchecks; j++)
|
|
{
|
|
if (convex_check(vp1, j, j + nchecks - 1))
|
|
{
|
|
vpn.push_back(vp1[j]);
|
|
}
|
|
else
|
|
{
|
|
vps_1.push_back(vpn); vpn.clear();
|
|
}
|
|
}
|
|
vps_1.push_back(vpn);
|
|
}
|
|
else
|
|
{
|
|
cout << "==== small arc I ====" << endl;
|
|
}
|
|
}
|
|
for (i = 0; i < points_2.size(); i++)
|
|
{
|
|
VP& vp1 = points_2[i];
|
|
if (vp1.size() > nchecks)
|
|
{
|
|
VP vpn;
|
|
for (j = 0; j <= vp1.size() - nchecks; j++)
|
|
{
|
|
if (concave_check(vp1, j, j + nchecks - 1))
|
|
{
|
|
vpn.push_back(vp1[j]);
|
|
}
|
|
else
|
|
{
|
|
vps_2.push_back(vpn); vpn.clear();
|
|
}
|
|
}
|
|
vps_2.push_back(vpn);
|
|
}
|
|
else
|
|
{
|
|
cout << "==== small arc II ====" << endl;
|
|
}
|
|
}
|
|
Mat3b out2(480, 640, Vec3b(0, 0, 0));
|
|
for (i = 0; i < vps_2.size(); ++i)
|
|
{
|
|
// Vec3b color(rand()%255, 128+rand()%127, 128+rand()%127);
|
|
Vec3b color(255, 0, 0);
|
|
for (j = 0; j < vps_2[i].size(); ++j)
|
|
out2(vps_2[i][j]) = color;
|
|
}
|
|
imshow("out2", out2); waitKey();
|
|
for (i = 0; i < points_3.size(); i++)
|
|
{
|
|
VP& vp1 = points_3[i];
|
|
if (vp1.size() > nchecks)
|
|
{
|
|
VP vpn;
|
|
for (j = 0; j <= vp1.size() - nchecks; j++)
|
|
{
|
|
if (concave_check(vp1, j, j + nchecks - 1))
|
|
{
|
|
vpn.push_back(vp1[j]);
|
|
}
|
|
else
|
|
{
|
|
vps_3.push_back(vpn); vpn.clear();
|
|
}
|
|
}
|
|
vps_3.push_back(vpn);
|
|
}
|
|
else
|
|
{
|
|
cout << "==== small arc III ====" << endl;
|
|
}
|
|
}
|
|
for (i = 0; i < points_4.size(); i++)
|
|
{
|
|
VP& vp1 = points_4[i];
|
|
if (vp1.size() > nchecks)
|
|
{
|
|
VP vpn;
|
|
for (j = 0; j <= vp1.size() - nchecks; j++)
|
|
{
|
|
if (convex_check(vp1, j, j + nchecks - 1))
|
|
{
|
|
vpn.push_back(vp1[j]);
|
|
}
|
|
else
|
|
{
|
|
vps_4.push_back(vpn); vpn.clear();
|
|
}
|
|
}
|
|
vps_4.push_back(vpn);
|
|
}
|
|
else
|
|
{
|
|
cout << "==== small arc IV ====" << endl;
|
|
}
|
|
}
|
|
points_1 = vps_1; points_2 = vps_2; points_3 = vps_3; points_4 = vps_4;
|
|
}
|
|
|
|
}
|
|
|