参考任意多边形的最大内切圆算法_海风吹来的石头的博客-CSDN博客_c++ 最大内切圆
利用opencv的distanceTransform函数实现内切圆,对于稍微大一点的图像,耗时很严重,所以我参考上面提到的博客的思想(将轮廓区域进行分割),主要利用opencv的pointPolygonTest函数来求解内切圆,耗时较少。所以在这里记录一下。
代码如下(示例):
//可牺牲精度加快速度
Point GeometryFindPIA(const vector &polygon, float bounds[]) {
int N_CELLS = 1;
int M_CELLS = 5;
cv::Point pia;
pia.x = (bounds[0] + bounds[1]) / 2;
pia.y = (bounds[2] + bounds[3]) / 2;
cv::Point tmp;
float increment_x = (bounds[1] - bounds[0]) / N_CELLS;
float increment_y = (bounds[3] - bounds[2]) / M_CELLS;
float max_distance = 0;
int i, j;
float tmp_distance = FLT_MAX;
for (i = 0; i <= N_CELLS; i++)
{
tmp.x = bounds[0] + i * increment_x;
for (j = 0; j <= M_CELLS; j++)
{
tmp.y = bounds[2] + j * increment_y;
tmp_distance = pointPolygonTest(polygon, tmp, true);
if (tmp_distance > max_distance)
{
max_distance = tmp_distance;
pia.x = tmp.x;
pia.y = tmp.y;
}
}
}
return pia;
}
//求解最大内切圆函数,传入多个轮廓
bool MaxInnerCircle(const vector> &cons, vector< Point> ¢er, vector &radius) {
if (cons.size() == 0) {
return false;
}
for (int m = 0; m < cons.size(); ++m) {
Rect rect = boundingRect(cons[m]);//获得轮廓的矩形区域
double radiusTemp = 0;
float bounds[4];
//初始化区域
bounds[0] = rect.x;
bounds[1] = rect.x + rect.width;
bounds[2] = rect.y;
bounds[3] = rect.y + rect.height;
cv::Point point_pia;
float flt_tmp = FLT_MAX;
int count = 1;
cv::Point point_tmp;//
//先划分区域,找到合适的一个区域后,再次划分这个区域,按此迭代,直到找到心点和半径
while (count++)
{
// 对区域划分,找到最合适的区域
point_tmp = GeometryFindPIA(cons[m], bounds);
// 更新中心点
point_pia.x = point_tmp.x;
point_pia.y = point_tmp.y;
// 更新区域
flt_tmp = (bounds[1] - bounds[0]) / (sqrtf(2) * 2);
bounds[0] = point_pia.x - flt_tmp;
bounds[1] = point_pia.x + flt_tmp;
flt_tmp = (bounds[3] - bounds[2]) / (sqrtf(2) * 2);
bounds[2] = point_pia.y - flt_tmp;
bounds[3] = point_pia.y + flt_tmp;
//可牺牲精度加快速度
if (bounds[1] - bounds[0] < 1 || bounds[3] - bounds[2] < 1) break;
}
radiusTemp = pointPolygonTest(cons[m], point_pia, true);
if (radiusTemp>0) {
center.push_back(point_tmp);
radius.push_back(radiusTemp);
}
}
}
//最大内接圆重载,传入单个轮廓
bool MaxInnerCircle(const vector &con, Point ¢er, double &radius) {
if (con.size() <= 0) {
return false;
}
Rect rect = boundingRect(con);
float bounds[4];
bounds[0] = rect.x;
bounds[1] = rect.x + rect.width;
bounds[2] = rect.y;
bounds[3] = rect.y + rect.height;
cv::Point point_pia;
float flt_tmp = FLT_MAX;
int count = 1;
cv::Point point_tmp;//
while (count++)
{
// find new candidate
point_tmp = GeometryFindPIA(con, bounds);
// update current
point_pia.x = point_tmp.x;
point_pia.y = point_tmp.y;
// update the bounds
flt_tmp = (bounds[1] - bounds[0]) / (sqrtf(2) * 2);
bounds[0] = point_pia.x - flt_tmp;
bounds[1] = point_pia.x + flt_tmp;
flt_tmp = (bounds[3] - bounds[2]) / (sqrtf(2) * 2);
bounds[2] = point_pia.y - flt_tmp;
bounds[3] = point_pia.y + flt_tmp;
//可牺牲精度加快速度
if (bounds[1] - bounds[0] < 1 || bounds[3] - bounds[2] < 1) break;
}
radius = pointPolygonTest(con, point_pia, true);
center = point_pia;
if (radius <= 0.1) {
return false;
}
return true;
}
代码如下(示例):
//测试最大内接圆 void testInnerCircle(int flag) { Mat img = imread("1.png", 1); Mat src; cvtColor(img, src, COLOR_BGR2GRAY); threshold(src, src, 130, 255, THRESH_BINARY); vector
> contours, SelectContours; findContours(src, contours, RETR_LIST, CHAIN_APPROX_NONE); int jpg = 10000000; for (int i = 0; i < contours.size(); i = i + 1) {//一条边内外轮廓会检测出四条 //approxPolyDP(Con, approx, 0.1, true); double area1 = contourArea(contours[i]); if (area1 > 1000) { SelectContours.push_back(contours[i]); } } clock_t begin = clock(); if (flag == 1) { vector Center; vector Radius; MaxInnerCircle(SelectContours, Center, Radius); for (int i = 0; i < Center.size(); i++) { circle(img, Center[i], Radius[i], Scalar(0, 0, 255),6); } } else { vector cons1; Point P; double R; for (int i = 0; i < SelectContours.size(); i++) { cons1 = SelectContours[i]; if (MaxInnerCircle(cons1, P, R)) { circle(img, P, R, Scalar(0, 0, 255), 2); } } } clock_t end = clock(); double programTimes = ((double)end - begin) / CLOCKS_PER_SEC; cout << "执行时间: " << programTimes << endl; namedWindow("img", WINDOW_NORMAL); namedWindow("src", WINDOW_NORMAL); imshow("img", img); imshow("src", src); waitKey(0); }