OpenCV(python) 找二值图像的轮廓。轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。轮廓是连续的,边缘并不全都图边缘包括轮廓。在 OpenCV 中其实边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手,而轮廓主要用来分析物体的形态,比如物体的周长和面轮廓。
加载图像
1
| img_url = "https://github.com/xujinzh/archive/blob/master/images/opencv/9.png?raw=true"
|
1 2 3 4 5
| import cv2 import matplotlib.pyplot as plt import numpy as np import requests from PIL import Image
|
1 2 3 4 5 6 7
| response = requests.get(img_url, stream=True) img = np.array(Image.open(response.raw).convert("RGB"))
img_init = img.copy() print(img.shape) plt.imshow(img) plt.show()
|
(225, 224, 3)
转化为二值图像
1 2 3 4 5 6
| img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) plt.imshow(thresh, cmap="gray") plt.show()
|
1 2
| set(thresh.reshape(1, -1).tolist()[0])
|
{0, 255}
find Contours
1
| contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
2
1
| len(hierarchy), type(hierarchy)
|
(1, numpy.ndarray)
array([[[-1, -1, 1, -1],
[-1, -1, -1, 0]]], dtype=int32)
1 2 3
| for cnt in contours: print(type(cnt))
|
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
画 contours
1 2 3 4 5
| img_contours = cv2.drawContours(img, contours, -1, (0, 255, 0), 2)
plt.imshow(img_contours) plt.show()
|
1 2 3 4 5 6
| for cnt in contours: img = img_init.copy() cv2.drawContours(img, [cnt], 0, (255, 0, 255), 2) plt.imshow(img) plt.show()
|
计算轮廓面积
1 2
| for cnt in contours: print(cv2.contourArea(cnt))
|
10624.0
3585.5
计算周长
1 2
| for cnt in contours: print(cv2.arcLength(cnt, True))
|
530.6173120737076
239.17871356010437
计算矩
1 2 3 4 5 6
| for cnt in contours: M = cv2.moments(cnt) print(M) cx, cy = M["m10"] / M["m00"], M["m01"] / M["m00"] print(cx, cy)
|
{'m00': 10624.0, 'm10': 1179807.0, 'm01': 988628.0, 'm20': 138264122.0, 'm11': 110420621.75, 'm02': 108261792.33333333, 'm30': 16915839222.900002, 'm21': 12973061316.9, 'm12': 12258148255.633333, 'm03': 13524838384.2, 'mu20': 7245244.246893823, 'mu11': 632384.2880270928, 'mu02': 16263926.89809236, 'mu30': -47729935.87674332, 'mu21': -33712518.19269228, 'mu12': 117861541.09828925, 'mu03': 423503079.8199959, 'nu20': 0.06419140872094325, 'nu11': 0.005602797768874818, 'nu02': 0.1440951254846342, 'nu30': -0.00410270882762122, 'nu21': -0.0028978175530693143, 'nu12': 0.01013099171869212, 'nu03': 0.036402936483911824}
111.05111069277109 93.05609939759036
{'m00': 3585.5, 'm10': 387701.3333333333, 'm01': 283841.5, 'm20': 43129861.08333333, 'm11': 30132658.208333332, 'm02': 23606262.25, 'm30': 4925989623.3, 'm21': 3294219072.0333333, 'm12': 2462194527.1, 'm03': 2049563404.8500001, 'mu20': 1207584.171202682, 'mu11': -559219.6344593987, 'mu02': 1136314.6493166909, 'mu30': 1190115.5424232483, 'mu21': 836261.0243297517, 'mu12': -1818823.7834247053, 'mu03': 894210.7344172001, 'nu20': 0.0939329492050244, 'nu11': -0.04349936904672335, 'nu02': 0.0883891895741609, 'nu30': 0.0015460189391694732, 'nu21': 0.0010863444225512572, 'nu12': -0.0023627420329802268, 'nu03': 0.0011616239614875813}
108.13033979454283 79.1637149630456
轮廓外接矩形
1 2 3 4 5 6 7 8 9
| for cnt in contours: x, y, w, h = cv2.boundingRect(cnt) img = img_init.copy() cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) rect = cv2.minAreaRect(cnt) box = np.intp(cv2.boxPoints(rect)) cv2.drawContours(img, [box], 0, (255, 0, 0), 2) plt.imshow(img) plt.show()
|
轮廓外接圆
1 2 3 4 5 6 7
| for cnt in contours: img = img_init.copy() (x, y), radius = cv2.minEnclosingCircle(cnt) (x, y, radius) = np.intp((x, y, radius)) cv2.circle(img, (x, y), radius, (0, 0, 255), 2) plt.imshow(img) plt.show()
|
轮廓拟合椭圆
1 2 3 4 5 6
| for cnt in contours: img = img_init.copy() ellipse = cv2.fitEllipse(cnt) cv2.ellipse(img, ellipse, (255, 255, 0), 2) plt.imshow(img) plt.show()
|
轮廓匹配度
1
| cv2.matchShapes(contours[0], contours[1], 1, 0.0)
|
0.2869094336215315
参考文献
- cv2.findContours() - 轮廓提取算法 – OpenCV