参考python-opencv 图像捕捉多个不规则轮廓,与轮廓内接区域(圆/矩形)思路-持续更新编辑中(会附上详细的思路解释和图片) - Lorzen - 博客园
提示:利用中心延展算法的思想,加入快扩散慢收缩,加快运行速度。由于使用矩来求轮廓的重心,并作为扩散的中心点,所以对u形的区域不友好。
以下是本篇文章正文内容,下面案例可供参考
代码如下(示例):
//最大内接矩形,传入单个轮廓 //利用中心延展算法的思想,加入快扩散慢收缩。加快运行速度 bool MaxInnerRect(const vector
&con, Rect &R) { Moments moment = moments(con); //由于使用矩来求轮廓的重心,并作为扩散的中心点,所以对u形的区域不友好, Point midPoint = Point(int(moment.m10 / moment.m00), int(moment.m01 / moment.m00)); double Flag = pointPolygonTest(con, midPoint, false); if (Flag <= 0) { R.x = 0; R.y = 0; R.width = 0; R.height = 0; return false; } Rect rect = boundingRect(con); int x1, x2, y1, y2;//中心扩散的四个顶点坐标 x1 = midPoint.x - 1; x2 = midPoint.x + 1; y1 = midPoint.y - 1; y2 = midPoint.y + 1; //*********// int cnt_range, step, radio_x, radio_y; int thre = 4;//由于轮廓并非是很直的线,一个突出点会影响结果,所以要设置敏感值,太小结果不对 if (rect.width > rect.height) {// 判断轮廓 X方向更长 cnt_range = rect.width; //获得长边 } else { cnt_range = rect.height; } bool flag_x1 = false, flag_x2 = false, flag_y1 = false, flag_y2 = false;//定义标志,标记是否到达边缘 radio_x = rect.width / 6;//根据区域的外接矩形边来决定x边扩散的步长 radio_y = rect.height / 6;//根据区域的外接矩形边来决定y边扩散的步长 step = cnt_range/120;// if (step<1) { //收缩时步长等于1;step代表每次收缩时,判断点在轮廓外面的步长,必须大于等于1,同时也不能太大,会影响内接矩阵的精度 step = 1; } if (step>=3) { step = 2; } //四条边分别求,每条边找到第一次出现在轮廓外的点,然后再收缩,直到找到边界 //(此处为了让边不要太敏感,设置出现在轮廓外的点大于thre时,才开始收缩) for (int ix = 1; ix < cnt_range; ++ix) { if (flag_y1 == false) { y1 = y1 - radio_y;//每次扩散radio_y行 int numy1 = 0; for (int i = x1; i <= x2;i=i+ step) { double p_x1y1 = pointPolygonTest(con, Point(i, y1), false); if (p_x1y1 < 0) { numy1++; } if (numy1 > thre || y1 <= rect.y) {//开始收缩 for (int m = 0; m < radio_y -1 ; ++m) { int numy11 = 0; y1 = y1+ 1; // 每次收缩1行 for (int j = x1; j <= x2; j=j+ step) { double p_jy1 = pointPolygonTest(con, Point(j, y1), false); if (p_jy1 < 0) { numy11++; } if (numy11 > thre) break;//继续收缩 } if (numy11 > thre) continue;//继续收缩 else break;//找到结果收缩结束 } flag_y1 = true; break;//找到结果收缩结束 } } } if (flag_x1 == false) { x1 = x1 - radio_x; int numx1 = 0; for (int i = y1; i <= y2; i = i + step) { double p_x1i = pointPolygonTest(con, Point(x1, i), false); if (p_x1i < 0) { numx1++; } if (numx1 > thre || x1<= rect.x) {//开始收缩 for (int m = 0; m < radio_x - 1; ++m) { int numx11 = 0; x1 = x1 + 1; //每次收缩1行 for (int j = y1; j <= y2; j = j + step) { double p_x1j = pointPolygonTest(con, Point(x1, j), false); if (p_x1j < 0) { numx11++; } if (numx11 > thre) break; } if (numx11 > thre) continue; else break; } flag_x1 = true; break; } } } if (flag_y2 == false) { y2 = y2 + radio_y; int numy2 = 0; for (int i = x1; i <= x2; i =i + step) { double p_iy2 = pointPolygonTest(con, Point(i, y2), false); if (p_iy2 < 0) { numy2++; } if (numy2 > thre || y2 >= (rect.y+rect.height)) {开始收缩 for (int m = 0; m < radio_y - 1; ++m) { int numy22 = 0; y2 = y2 - 1; // 每次收缩1行 for (int j = x1; j <= x2; j = j + step) { double p_jy2 = pointPolygonTest(con, Point(j, y2), false); if (p_jy2 < 0) { numy22++; } if (numy22 > thre) break; } if (numy22 > thre) continue; else break; } flag_y2 = true; break; } } } if (flag_x2 == false) { x2 = x2 + radio_x; int numx2 = 0; for (int i = y1; i <= y2; i = i + step) { double p_x2i = pointPolygonTest(con, Point(x2, i), false); if (p_x2i < 0) { numx2++; } if (numx2 > thre || x2 >= (rect.x + rect.width)) {//开始收缩 for (int m = 0; m < radio_x - 1; ++m) { int numx22 = 0; x2 = x2 - 1; // 每次收缩1行 for (int j = y1; j <= y2; j=j+ step) { double p_x2j = pointPolygonTest(con, Point(x2, j), false); if (p_x2j < 0) { numx22++; } if (numx22 > thre) break; } if (numx22 > thre) continue; else break; } flag_x2 = true; break; } } } if (flag_y1 && flag_x1 && flag_y2 && flag_x2) { break; } } R.x = x1; R.y = y1; R.width = x2 - x1; R.height = y2 - y1; return true; } //最大内接矩形重载,传入多个轮廓 bool MaxInnerRect(const vector
> &cons, vector &R) { Rect r; if (cons.size() == 0) { return false; } for (int i = 0; i < cons.size(); i++) { if (MaxInnerRect(cons[i], r)) { R.push_back(r); } } if (R.size() == 0) { return false; } return true; }
代码如下(示例):
void testInnerRect(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;
int png = 3000;
clock_t begin = clock();
if (flag == 1) {
vector R;
for (int i = 0; i < contours.size(); i = i + 1) {
double area1 = contourArea(contours[i]);
if (area1 > png && area1< 500000) {
SelectContours.push_back(contours[i]);
}
}
if (MaxInnerRect(SelectContours, R)) {
for (int m = 0; m < R.size();m++) {
rectangle(img, R[m], Scalar(0, 0, 255), 3);
}
}
}
else {
for (int i = 0; i < contours.size(); i = i + 1) {//
double area1 = contourArea(contours[i]);
if (area1 > jpg) {
SelectContours.push_back(contours[i]);
}
}
vector cons1;
Rect R;
for (int i = 0; i < SelectContours.size(); i++) {
cons1 = SelectContours[i];
if (MaxInnerRect(cons1, R)) {
rectangle(img, R, Scalar(0, 0, 255), 3);
}
}
}
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);
}
有改善之处还望提出,共勉!