python - 在 Python OpenCV 中查找最大和第二大面积轮廓

标签 python opencv

我正在研究一个 Python OpenCV 脚本,它需要在图像上找到特定颜色的最大和第二大形状。为此,让我们看看下面的示例图像。我想确定她的两个浅米色矩形的坐标:
Sample image
我设法获得了面具上出现的两个轮廓:

img_path = "path\\to\\file.png"
img = cv2.imread(img_path)
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(imgHSV, np.array([21,7,240]), np.array([21,7,255]))
contours = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
上面的代码返回了以下列表列表:
[array([[[  8, 245]], [[  7, 246]], [[  6, 246]], [[  6, 247]], [[  5, 248]], [[  0, 248]], [[  0, 300]], [[676, 300]], [[676, 248]], [[675, 247]], [[675, 246]], [[674, 246]], [[673, 245]]], dtype=int32), 
array([[[ 19,  22]], [[ 18,  23]], [[ 18,  24]], [[ 17,  25]], [[ 17, 120]], [[ 18, 121]], [[ 18, 122]], [[ 19, 123]], [[658, 123]], [[659, 122]], [[661, 122]], [[661,  23]], [[659,  23]], [[658,  22]]], dtype=int32)]
现在,确定形状后,我想以编程方式找到第一和第二大形状的质心。现在很容易,因为只有两个这样的形状,但在实践中,我可能需要在同一张图像上处理几十个。
我知道cv2.contourArea(c)返回轮廓的面积和 cv2.moments(c)返回它的质心(在这两种情况下 c 表示 contours 列表中的一个元素)。
我尝试的方法如下:
  • 使用 contour 在一个数据框中组织轮廓和区域和 area栏目
  • 通过 max(df['area']) 找到最大面积
  • 找到面积最大的对应dataframe['contour']
  • 得到它的质心

  • 如果可行,这将解决问题的前半部分,即找到面积最大的质心:
    contour_area = list()
    for c in contours:
        contour_area.append(cv2.contourArea(c))
        M = cv2.moments(c)
    df = pd.DataFrame(
        {'contour': contours,
         'area': contour_area
        })
    largest_contour = df.loc[df['area'] == max(df['area']) ]['contour']
    centroid = cv2.moments(largest_contour)
    
    但是在运行它时,我得到了 Expected Ptr<cv::UMat> for argument 'array'最后一行的错误信息。我赶紧查了一下数据类型,发现原来是contour元素的数据类型为 <class 'numpy.ndarray'>而我的largest_contour元素现在的数据类型为 <class 'pandas.core.series.Series'> .所以我把最后一行改成:
    centroid = cv2.moments(largest_contour.to_numpy())
    
    现在确保 largest_contour元素具有与 <class 'numpy.ndarray'> 相同的数据类型 ( contour )元素了。但是,当重新运行代码时,我得到了完全相同的错误消息:Expected Ptr<cv::UMat> for argument 'array' .
    对于如何前进的任何帮助或提示,我将不胜感激!

    最佳答案

    这是在 Python/OpenCV 中执行此操作的一种方法。

  • 阅读输入
  • 转换为 HSV 颜色空间
  • 颜色阈值
  • 获取轮廓
  • 为所有轮廓创建 (index, area, cx, cy) 列表
  • 以相反的顺序对区域列表进行排序
  • 打印前两个轮廓的索引、面积、cx、cy 值
  • 用不同颜色绘制两条轮廓
  • 保存结果

  • 输入:
    enter image description here
    import cv2
    import numpy as np
    
    # read image
    img = cv2.imread('rectangles2.png')
    
    # convert to HSV
    imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
    # threshold on color
    lower=(21,7,240)
    upper=(21,7,255)
    thresh = cv2.inRange(imgHSV, lower, upper)
    
    # get contours
    result = img.copy() 
    cntrs_info = []
    contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    index=0
    for cntr in contours:
        area = cv2.contourArea(cntr)
        M = cv2.moments(cntr)
        cx = int(M["m10"] / M["m00"])
        cy = int(M["m01"] / M["m00"])
        cntrs_info.append((index,area,cx,cy))
        index = index + 1
        #print(index,area,cx,cy)
    
    # sort contours by area
    def takeSecond(elem):
        return elem[1]
    cntrs_info.sort(key=takeSecond, reverse=True)
    
    # get index, area and centroid for first two sorted contours
    index_first = cntrs_info[0][0]
    area_first = cntrs_info[0][1]
    cx_first = cntrs_info[0][2]
    cy_first = cntrs_info[0][3]
    print("index1:", index_first, "area1:", area_first, "cx1:", cx_first, "cy1:", cy_first)
    cv2.drawContours(result,[contours[index_first]],0,(0,0,255),1)
    
    index_second = cntrs_info[1][0]
    area_second = cntrs_info[1][1]
    cx_second = cntrs_info[1][2]
    cy_second = cntrs_info[1][3]
    print("index2:", index_second, "area2:", area_second, "cx2:", cx_second, "cy2:", cy_second)
    cv2.drawContours(result,[contours[index_second]],0,(0,255,0),1)
    
    # save results
    cv2.imwrite('rectangles2_thresh.png',thresh)
    cv2.imwrite('rectangles2_contours.png',result)
    
    # show results
    cv2.imshow("thresh", thresh)
    cv2.imshow("result", result)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    阈值图像:
    enter image description here
    最大的两个轮廓:
    enter image description here
    文字资料:
    index1: 1 area1: 65033.0 cx1: 338 cy1: 71
    index2: 0 area2: 37568.0 cx2: 339 cy2: 272
    

    关于python - 在 Python OpenCV 中查找最大和第二大面积轮廓,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63209402/

    相关文章:

    c++ - 为 const 数据创建 cv::Mat header

    python - numpy.mod() 和 numpy.remainder() 有什么区别?

    来自不同项目的 Python 导入包

    python - 使用 RegEx 解析 NOAAPORT feed

    python - 获取函数的原始定义模块

    c++ - 如何计算 OpenCV 中矩阵的直方图?

    opencv - 如何加速LBP纹理匹配

    python - 在 Python 中使用过滤器从文本文件中过滤行

    c++ - 使用 cv::Mat 的高效 C++ 四元数乘法

    python - 如何比较两个一个热编​​码列表?