我正在研究一个 Python OpenCV 脚本,它需要在图像上找到特定颜色的最大和第二大形状。为此,让我们看看下面的示例图像。我想确定她的两个浅米色矩形的坐标:
我设法获得了面具上出现的两个轮廓:
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'])
找到最大面积如果可行,这将解决问题的前半部分,即找到面积最大的质心:
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 中执行此操作的一种方法。
输入:
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()
阈值图像:
最大的两个轮廓:
文字资料:
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/