WHCSRL 技术网

opencv 最大内接矩形笔记

python 最小外接矩形:

  1. cnt = np.array([[data_0_x, data_0_y], [data_1_x, data_1_y], [data_2_x, data_2_y], [data_3_x, data_3_y]]) # 必须是array数组的形式
  2. rect = cv2.minAreaRect(cnt) # 得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
  3. box = cv2.boxPoints(rect) # 获取最小外接矩形的4个顶点坐标(ps: cv2.boxPoints(rect) for OpenCV 3.x)
  4. box = np.int0(box)
  5. cv2.drawContours(img, [box], 0, (255, 0, 0), 1)

最大内接矩形,从轮廓中所有坐标中获取其中4个坐标即可:

python 获取过程如下:

转自:图像轮廓最大内接矩形的求法 - 奥布莱恩 - 博客园

  1. def order_points(pts):
  2. # pts为轮廓坐标
  3. # 列表中存储元素分别为左上角,右上角,右下角和左下角
  4. rect = np.zeros((4, 2), dtype = "float32")
  5. # 左上角的点具有最小的和,而右下角的点具有最大的和
  6. s = pts.sum(axis = 1)
  7. rect[0] = pts[np.argmin(s)]
  8. rect[2] = pts[np.argmax(s)]
  9. # 计算点之间的差值
  10. # 右上角的点具有最小的差值,
  11. # 左下角的点具有最大的差值
  12. diff = np.diff(pts, axis = 1)
  13. rect[1] = pts[np.argmin(diff)]
  14. rect[3] = pts[np.argmax(diff)]
  15. # 返回排序坐标(依次为左上右上右下左下)
  16. return rect
  17. img = cv2.imread(path)
  18. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  19. blurred = cv2.blur(gray, (9, 9))
  20. _, thresh = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY)
  21. _, cnts, _ = cv2.findContours( thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
  22. c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]<br>先找出轮廓点
  23. rect = order_points(c.reshape(c.shape[0], 2))
  24. print(rect)
  25. xs = [i[0] for i in rect]
  26. ys = [i[1] for i in rect]
  27. xs.sort()
  28. ys.sort()
  29. #内接矩形的坐标为
  30. print(xs[1],xs[2],ys[1],ys[2])

一下内容转自:

python-opencv 图像捕捉多个不规则轮廓,与轮廓内接区域(圆/矩形)思路-持续更新编辑中(会附上详细的思路解释和图片) - Lorzen - 博客园

  1. def drawInRectgle(img, cont, cX, cY, x_min, x_max, y_min, y_max):
  2. """绘制不规则最大内接正矩形"""
  3. # img 对应的是原图, 四个极值坐标对应的是最大外接矩形的四个顶点
  4. c = cont # 单个轮廓
  5. # print(c)
  6. range_x, range_y = x_max - x_min, y_max - y_min # 轮廓的X,Y的范围
  7. x1, x2, y1, y2 = cX, cX, cY, cY # 中心扩散矩形的四个顶点x,y
  8. cnt_range, radio = 0, 0
  9. shape_flag = 1 # 1:轮廓X轴方向比Y长;0:轮廓Y轴方向比X长
  10. if range_x > range_y: # 判断轮廓 X方向更长
  11. radio, shape_flag = int(range_x / range_y), 1
  12. range_x_left = cX - x_min
  13. range_x_right = x_max - cX
  14. if range_x_left >= range_x_right: # 取轴更长范围作for循环
  15. cnt_range = int(range_x_left)
  16. if range_x_left < range_x_right:
  17. cnt_range = int(range_x_right)
  18. else: # 判断轮廓 Y方向更长
  19. radio, shape_flag = int(range_y / range_x), 0
  20. range_y_top = cY - y_min
  21. range_y_bottom = y_max - cY
  22. if range_y_top >= range_y_bottom: # 取轴更长范围作for循环
  23. cnt_range = int(range_y_top)
  24. if range_y_top < range_y_bottom:
  25. cnt_range = int(range_y_bottom)
  26. print("X radio Y: %%d " %% radio)
  27. print("---------new drawing range: %%d-------------------------------------" %% cnt_range)
  28. flag_x1, flag_x2, flag_y1, flag_y2 = False, False, False, False
  29. radio = 5 # 暂时设5,统一比例X:Y=5:1 因为发现某些会出现X:Y=4:1, 某些会出现X:Y=5:1
  30. if shape_flag == 1:
  31. radio_x = radio - 1
  32. radio_y = 1
  33. else:
  34. radio_x = 1
  35. radio_y = radio - 1
  36. for ix in range(1, cnt_range, 1): # X方向延展,假设X:Y=3:1,那延展步进值X:Y=3:1
  37. # 第二象限延展
  38. if flag_y1 == False:
  39. y1 -= 1 * radio_y # 假设X:Y=1:1,轮廓XY方向长度接近,可理解为延展步进X:Y=1:1
  40. p_x1y1 = cv.pointPolygonTest(c, (x1, y1), False)
  41. p_x2y1 = cv.pointPolygonTest(c, (x2, y1), False)
  42. if p_x1y1 <= 0 or y1 <= y_min or p_x2y1 <= 0: # 在轮廓外,只进行y运算,说明y超出范围
  43. for count in range(0, radio_y - 1, 1): # 最长返回步进延展
  44. y1 += 1 # y超出, 步进返回
  45. p_x1y1 = cv.pointPolygonTest(c, (x1, y1), False)
  46. if p_x1y1 <= 0 or y1 <= y_min or p_x2y1 <= 0:
  47. pass
  48. else:
  49. break
  50. # print("y1 = %%d, P=%%d" %% (y1, p_x1y1))
  51. flag_y1 = True
  52. if flag_x1 == False:
  53. x1 -= 1 * radio_x
  54. p_x1y1 = cv.pointPolygonTest(c, (x1, y1), False) # 满足第二象限的要求,像素都在轮廓内
  55. p_x1y2 = cv.pointPolygonTest(c, (x1, y2), False) # 满足第三象限的要求,像素都在轮廓内
  56. if p_x1y1 <= 0 or x1 <= x_min or p_x1y2 <= 0: # 若X超出轮廓范围
  57. # x1 += 1 # x超出, 返回原点
  58. for count in range(0, radio_x-1, 1): #
  59. x1 += 1 # x超出, 步进返回
  60. p_x1y1 = cv.pointPolygonTest(c, (x1, y1), False) # 满足第二象限的要求,像素都在轮廓内
  61. p_x1y2 = cv.pointPolygonTest(c, (x1, y2), False) # 满足第三象限的要求,像素都在轮廓内
  62. if p_x1y1 <= 0 or x1 <= x_min or p_x1y2 <= 0:
  63. pass
  64. else:
  65. break
  66. # print("x1 = %%d, P=%%d" %% (x1, p_x1y1))
  67. flag_x1 = True # X轴像左延展达到轮廓边界,标志=True
  68. # 第三象限延展
  69. if flag_y2 == False:
  70. y2 += 1 * radio_y
  71. p_x1y2 = cv.pointPolygonTest(c, (x1, y2), False)
  72. p_x2y2 = cv.pointPolygonTest(c, (x2, y2), False)
  73. if p_x1y2 <= 0 or y2 >= y_max or p_x2y2 <= 0: # 在轮廓外,只进行y运算,说明y超出范围
  74. for count in range(0, radio_y - 1, 1): # 最长返回步进延展
  75. y2 -= 1 # y超出, 返回原点
  76. p_x1y2 = cv.pointPolygonTest(c, (x1, y2), False)
  77. if p_x1y2 <= 0 or y2 >= y_max or p_x2y2 <= 0: # 在轮廓外,只进行y运算,说明y超出范围
  78. pass
  79. else:
  80. break
  81. # print("y2 = %%d, P=%%d" %% (y2, p_x1y2))
  82. flag_y2 = True # Y轴像左延展达到轮廓边界,标志=True
  83. # 第一象限延展
  84. if flag_x2 == False:
  85. x2 += 1 * radio_x
  86. p_x2y1 = cv.pointPolygonTest(c, (x2, y1), False) # 满足第一象限的要求,像素都在轮廓内
  87. p_x2y2 = cv.pointPolygonTest(c, (x2, y2), False) # 满足第四象限的要求,像素都在轮廓内
  88. if p_x2y1 <= 0 or x2 >= x_max or p_x2y2 <= 0:
  89. for count in range(0, radio_x - 1, 1): # 最长返回步进延展
  90. x2 -= 1 # x超出, 返回原点
  91. p_x2y1 = cv.pointPolygonTest(c, (x2, y1), False) # 满足第一象限的要求,像素都在轮廓内
  92. p_x2y2 = cv.pointPolygonTest(c, (x2, y2), False) # 满足第四象限的要求,像素都在轮廓内
  93. if p_x2y1 <= 0 or x2 >= x_max or p_x2y2 <= 0:
  94. pass
  95. elif p_x2y2 > 0:
  96. break
  97. # print("x2 = %%d, P=%%d" %% (x2, p_x2y1))
  98. flag_x2 = True
  99. if flag_y1 and flag_x1 and flag_y2 and flag_x2:
  100. print("(x1,y1)=(%%d,%%d)" %% (x1, y1))
  101. print("(x2,y2)=(%%d,%%d)" %% (x2, y2))
  102. break
  103. # cv.line(img, (x1,y1), (x2,y1), (255, 0, 0))
  104. cv.rectangle(img, (x1, y1), (x2, y2), (255, 255, 255), 1, 8)
  105. return x1, x2, y1, y2

c++版

OpenCVSharp 小练习 最大内接矩形_tfarcraw的博客-CSDN博客

​​​​​​opencv:求区域的内接矩形_cfqcfqcfqcfqcfq的博客-CSDN博客_opencv 内接矩形

  1. #include<opencv2opencv.hpp>
  2. #include <iostream>
  3. #include<vector>
  4. using namespace cv;
  5. using namespace std;
  6. /**
  7. * @brief expandEdge 扩展边界函数
  8. * @param img:输入图像,单通道二值图,深度为8
  9. * @param edge 边界数组,存放4条边界值
  10. * @param edgeID 当前边界号
  11. * @return 布尔值 确定当前边界是否可以扩展
  12. */
  13. bool expandEdge(const Mat & img, int edge[], const int edgeID)
  14. {
  15. //[1] --初始化参数
  16. int nc = img.cols;
  17. int nr = img.rows;
  18. switch (edgeID) {
  19. case 0:
  20. if (edge[0]>nr)
  21. return false;
  22. for (int i = edge[3]; i <= edge[1]; ++i)
  23. {
  24. if (img.at<uchar>(edge[0], i) == 255)//遇见255像素表明碰到边缘线
  25. return false;
  26. }
  27. edge[0]++;
  28. return true;
  29. break;
  30. case 1:
  31. if (edge[1]>nc)
  32. return false;
  33. for (int i = edge[2]; i <= edge[0]; ++i)
  34. {
  35. if (img.at<uchar>(i, edge[1]) == 255)//遇见255像素表明碰到边缘线
  36. return false;
  37. }
  38. edge[1]++;
  39. return true;
  40. break;
  41. case 2:
  42. if (edge[2]<0)
  43. return false;
  44. for (int i = edge[3]; i <= edge[1]; ++i)
  45. {
  46. if (img.at<uchar>(edge[2], i) == 255)//遇见255像素表明碰到边缘线
  47. return false;
  48. }
  49. edge[2]--;
  50. return true;
  51. break;
  52. case 3:
  53. if (edge[3]<0)
  54. return false;
  55. for (int i = edge[2]; i <= edge[0]; ++i)
  56. {
  57. if (img.at<uchar>(i, edge[3]) == 255)//遇见255像素表明碰到边缘线
  58. return false;
  59. }
  60. edge[3]--;
  61. return true;
  62. break;
  63. default:
  64. return false;
  65. break;
  66. }
  67. }
  68. /**
  69. * @brief 求取连通区域内接矩
  70. * @param img:输入图像,单通道二值图,深度为8
  71. * @param center:最小外接矩的中心
  72. * @return 最大内接矩形
  73. * 基于中心扩展算法
  74. */
  75. cv::Rect InSquare(Mat &img, const Point center)
  76. {
  77. // --[1]参数检测
  78. if (img.empty() ||img.channels()>1|| img.depth()>8)
  79. return Rect();
  80. // --[2] 初始化变量
  81. int edge[4];
  82. edge[0] = center.y + 1;//top
  83. edge[1] = center.x + 1;//right
  84. edge[2] = center.y - 1;//bottom
  85. edge[3] = center.x - 1;//left
  86. //[2]
  87. // --[3]边界扩展(中心扩散法)
  88. bool EXPAND[4] = { 1,1,1,1 };//扩展标记位
  89. int n = 0;
  90. while (EXPAND[0] || EXPAND[1] || EXPAND[2] || EXPAND[3])
  91. {
  92. int edgeID = n %% 4;
  93. EXPAND[edgeID] = expandEdge(img, edge, edgeID);
  94. n++;
  95. }
  96. //[3]
  97. //qDebug() << edge[0] << edge[1] << edge[2] << edge[3];
  98. Point tl = Point(edge[3], edge[0]);
  99. Point br = Point(edge[1], edge[2]);
  100. return Rect(tl, br);
  101. }
  102. int main()
  103. {
  104. bool isExistence = false;
  105. float first_area = 0;
  106. /// 加载源图像
  107. Mat src;
  108. src = imread("cen.bmp", 1);
  109. //src = imread("C:\Users\Administrator\Desktop\测试图片\xxx\20190308152516.jpg",1);
  110. //src = imread("C:\Users\Administrator\Desktop\测试图片\xx\20190308151912.jpg",1);
  111. //src = imread("C:\Users\Administrator\Desktop\测试图像\2\BfImg17(x-247 y--91 z--666)-(492,280).jpg",1);
  112. cvtColor(src, src, CV_RGB2GRAY);
  113. threshold(src, src, 100, 255, THRESH_BINARY);
  114. Rect ccomp;
  115. Point center(src.cols / 2, src.rows / 2);
  116. //floodFill(src, center, Scalar(255, 255, 55), &ccomp, Scalar(20, 20, 20), Scalar(20, 20, 20));
  117. if (src.empty())
  118. {
  119. cout << "fali" << endl;
  120. }
  121. //resize(src, src, cv::Size(496, 460), cv::INTER_LINEAR);
  122. imshow("src", src);
  123. Rect rr = InSquare(src, center);
  124. rectangle(src, rr, Scalar(255), 1, 8);
  125. imshow("src2", src);
  126. waitKey(0);
  127. getchar();
  128. return 0;
  129. }

原图和效果图:

推荐阅读