#include <utility>
#include <algorithm>
+#include <math.h>
+
#include <cvaux.h>
#include <highgui.h>
-#define _SUBPIX_VERBOSE
+//#define _SUBPIX_VERBOSE
#undef max
float quantile_sum = total_sum*quantile;
for(int j = 0; j < hist.size[0]; j++)
{
- cur_sum += hist.at<float>(j);
+ cur_sum += hist.at<double>(j);
if(cur_sum > quantile_sum)
{
return j;
// temporary solution, have to make something more precise
corner = contour[min_idx];
return;
-
-#if 0
- // find the points corresponding to neighboring sides of the quadrangle
- int idx;
+}
- for(idx = min_idx; idx != min_idx; idx = (idx + 1) % contour.size())
+void findCorner(const vector<Point2f>& contour, Point2f point, Point2f& corner)
+{
+ // find the nearest point
+ double min_dist = std::numeric_limits<double>::max();
+ int min_idx = -1;
+
+ Rect brect = boundingRect(contour);
+
+ // find corner idx
+ for(size_t i = 0; i < contour.size(); i++)
{
- if(fabs(contour[idx].x - point.x) > brect.width*0.5f ||
- fabs(contour[idx].y - point.y) > brect.height*0.5f)
+ double dist = norm(contour[i] - point);
+ if(dist < min_dist)
{
- break;
+ min_dist = dist;
+ min_idx = i;
}
}
- assert(idx == min_idx);
- size_t idx1 = idx;
- vector<float> curve1;
- vector<Point2f> border_points1;
- for(size_t i = min_idx; i != idx1; i = (i + 1) % contour.size())
- {
- border_points1.push_back(contour[i]);
- }
- fitCurve2Order(border_points1, curve1);
+ assert(min_idx >= 0);
+
+ // temporary solution, have to make something more precise
+ corner = contour[min_idx];
+ return;
+}
+
+int segment_hist_max(const MatND& hist, int& low_thresh, int& high_thresh)
+{
+ Mat bw;
+ const double max_bell_width = 20; // we expect two bells with width bounded above
+ const double min_bell_width = 5; // and below
+
+ double total_sum = sum(hist).val[0];
+ double thresh = total_sum/(2*max_bell_width)*0.25f; // quarter of a bar inside a bell
+
+// threshold(hist, bw, thresh, 255.0, CV_THRESH_BINARY);
- for(idx = min_idx; idx != min_idx; idx = (idx - 1) % contour.size())
+ double quantile_sum = 0.0;
+ double min_quantile = 0.2;
+ double low_sum = 0;
+ double max_segment_length = 0;
+ int max_start_x = -1;
+ int max_end_x = -1;
+ int start_x = 0;
+ const double out_of_bells_fraction = 0.1;
+ for(int x = 0; x < hist.size[0]; x++)
{
- if(fabs(contour[idx].x - point.x) > brect.width*0.5f ||
- fabs(contour[idx].y - point.y) > brect.height*0.5f)
+ quantile_sum += hist.at<double>(x);
+ if(quantile_sum < 0.2*total_sum) continue;
+
+ if(quantile_sum - low_sum > out_of_bells_fraction*total_sum)
{
- break;
+ if(max_segment_length < x - start_x)
+ {
+ max_segment_length = x - start_x;
+ max_start_x = start_x;
+ max_end_x = x;
+ }
+
+ low_sum = quantile_sum;
+ start_x = x;
}
}
- assert(idx == min_idx);
- size_t idx2 = idx;
- vector<float> curve2;
- vector<Point2f> border_points2;
- for(size_t i = min_idx; i != idx2; i = (i - 1) % contour.size())
+
+ if(start_x == -1)
{
- border_points2.push_back(contour[i]);
+ return 0;
+ }
+ else
+ {
+ low_thresh = max_start_x + 0.25*(max_end_x - max_start_x);
+ high_thresh = max_start_x + 0.75*(max_end_x - max_start_x);
+ return 1;
}
- fitCurve2Order(border_points2, curve2);
-
- findCurvesCross(curve1, curve2, corner);
-#endif
}
-
-bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size region_size, std::vector<Point2f>& ground_truth_corners)
+
+bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size region_size)
{
const int nbins = 256;
float ranges[] = {0, 256};
Mat black_comp, white_comp;
for(size_t i = 0; i < corners.size(); i++)
- {
- size_t ground_truth_idx = 0;
- double min_ground_truth_dist = std::numeric_limits<double>::max();
- for(size_t idx = 0; idx < ground_truth_corners.size(); idx++)
- {
- float dist = norm(ground_truth_corners[idx] - corners[i]);
- if(dist < min_ground_truth_dist)
- {
- min_ground_truth_dist = dist;
- ground_truth_idx = idx;
- }
- }
-
+ {
int channels = 0;
- Rect roi(corners[i].x - region_size.width, corners[i].y - region_size.height, region_size.width*2, region_size.height*2);
+ Rect roi(corners[i].x - region_size.width, corners[i].y - region_size.height, region_size.width*2 + 1, region_size.height*2 + 1);
Mat img_roi = img(roi);
calcHist(&img_roi, 1, &channels, Mat(), hist, 1, &nbins, &_ranges);
+#if 0
int black_thresh = histQuantile(hist, 0.45f);
int white_thresh = histQuantile(hist, 0.55f);
+#else
+ int black_thresh, white_thresh;
+ segment_hist_max(hist, black_thresh, white_thresh);
+#endif
threshold(img, black_comp, black_thresh, 255.0, CV_THRESH_BINARY_INV);
threshold(img, white_comp, white_thresh, 255.0, CV_THRESH_BINARY);
- const int erode_count = 2;
+ const int erode_count = 1;
erode(black_comp, black_comp, Mat(), Point(-1, -1), erode_count);
erode(white_comp, white_comp, Mat(), Point(-1, -1), erode_count);
-#if 0
+#if defined(_SUBPIX_VERBOSE)
+ namedWindow("roi", 1);
+ imshow("roi", img_roi);
+ imwrite("test.jpg", img);
namedWindow("black", 1);
imshow("black", black_comp);
namedWindow("white", 1);
findContours(black_comp, black_contours, black_hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
findContours(white_comp, white_contours, white_hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
- if(black_contours.size() < 10 || white_contours.size() < 10) continue;
+ if(black_contours.size() < 5 || white_contours.size() < 5) continue;
// find two white and black blobs that are close to the input point
vector<std::pair<int, float> > white_order, black_order;
const vector<Point>* quads[4] = {&black_contours[black_order[0].first], &black_contours[black_order[1].first],
&white_contours[white_order[0].first], &white_contours[white_order[1].first]};
+ vector<Point2f> quads_approx[4];
Point2f quad_corners[4];
for(int k = 0; k < 4; k++)
{
+#if 1
+ vector<Point2f> temp;
+ for(size_t j = 0; j < quads[k]->size(); j++) temp.push_back((*quads[k])[j]);
+ approxPolyDP(temp, quads_approx[k], 0.5, true);
+
+ findCorner(quads_approx[k], corners[i], quad_corners[k]);
+#else
findCorner(*quads[k], corners[i], quad_corners[k]);
+#endif
quad_corners[k] += Point2f(0.5f, 0.5f);
}
- double old_dist = norm(corners[i] - ground_truth_corners[ground_truth_idx]);
// cross two lines
Point2f origin1 = quad_corners[0];
Point2f dir1 = quad_corners[1] - quad_corners[0];
Point2f origin2 = quad_corners[2];
Point2f dir2 = quad_corners[3] - quad_corners[2];
+ double angle = acos(dir1.dot(dir2)/(norm(dir1)*norm(dir2)));
+ if(cvIsNaN(angle) || cvIsInf(angle) || angle < 0.5 || angle > CV_PI - 0.5) continue;
+
findLinesCrossPoint(origin1, dir1, origin2, dir2, corners[i]);
-
- double new_dist = norm(corners[i] - ground_truth_corners[ground_truth_idx]);
#if defined(_SUBPIX_VERBOSE)
-#if 1
-// printf("Corner position: %f,%f, true position: %f,%f\n", corners[i].x, corners[i].y,
-// ground_truth_corners[ground_truth_idx].x, ground_truth_corners[ground_truth_idx].y);
- printf("Improved error for corner %d from %f to %f\n", (int)i, old_dist, new_dist);
-#endif
radius[i] = norm(corners[i] - ground_truth_corners[ground_truth_idx])*6;
-
+
#if 1
- Mat img1 = img(Rect(corners[i].x - 30, corners[i].y - 30, 60, 60));
- Mat test(img1.size(), CV_32FC3);
- cvtColor(img1, test, CV_GRAY2RGB);
- line(test, quad_corners[0] - corners[i] + Point2f(30, 30), quad_corners[1] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
- line(test, quad_corners[2] - corners[i] + Point2f(30, 30), quad_corners[3] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
+ Mat test(img.size(), CV_32FC3);
+ cvtColor(img, test, CV_GRAY2RGB);
+// line(test, quad_corners[0] - corners[i] + Point2f(30, 30), quad_corners[1] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
+// line(test, quad_corners[2] - corners[i] + Point2f(30, 30), quad_corners[3] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
vector<vector<Point> > contrs;
contrs.resize(1);
for(int k = 0; k < 4; k++)
{
- contrs[0] = *quads[k];
- drawContours(test, contrs, 0, CV_RGB(0, 0, 255), 1, 1, vector<Vec4i>(), 2, Point2f(30, 30) - corners[i]);
- circle(test, quad_corners[k] - corners[i] + Point2f(30, 30), 1, CV_RGB(255, 0, 0));
+ //contrs[0] = quads_approx[k];
+ contrs[0].clear();
+ for(size_t j = 0; j < quads_approx[k].size(); j++) contrs[0].push_back(quads_approx[k][j]);
+ drawContours(test, contrs, 0, CV_RGB(0, 0, 255), 1, 1, vector<Vec4i>(), 2);
+ circle(test, quad_corners[k], 0.5, CV_RGB(255, 0, 0));
}
+ Mat test1 = test(Rect(corners[i].x - 30, corners[i].y - 30, 60, 60));
namedWindow("1", 1);
- imshow("1", test);
+ imshow("1", test1);
imwrite("test.jpg", test);
waitKey(0);
#endif