Я работаю над сценарием Python OpenCV, который должен найти на изображении самую большую и вторую по величине формы определенного цвета c. Для этого давайте посмотрим на изображение ниже. Я хотел бы определить координаты двух ее бледно-бежевых прямоугольников:
Образец изображения
Мне удалось получить два контура, которые появляются на маске:
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'], площадь которого максимальна
- получить его центроид
Это, если бы это сработало, решило бы первую половину проблемы, то есть нахождение центроида с наибольшей площадью:
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'
.
Я был бы очень благодарен за любую помощь или подсказку о том, как двигаться дальше!