OpenCV 轮廓与属性:查找轮廓、面积周长、形状拟合与点测试
OpenCV 图像处理课程 · 第 7 章「Contours and its Properties」学习笔记
环境:Python 3.10 + opencv-python 4.5.5.64 + PyCharm(
chapter7目录)包含 4 课:查找轮廓 / 面积与周长 / 拟合形状 / 点在多边形内
章节总览
| 课 | 主题 | 核心函数 | 文件 |
|---|---|---|---|
| 01 | 查找轮廓 | cv2.findContours() / cv2.drawContours() |
find_contour.py |
| 02 | 面积与周长 | cv2.contourArea() / cv2.arcLength() |
contour_properties.py |
| 03 | 拟合形状 | cv2.boundingRect() / cv2.minEnclosingCircle() |
fitting_shape.py |
| 04 | 点在多边形内 | cv2.pointPolygonTest() |
point_check.py |
本章说明 :轮廓用于形状分析/物体定位------深度学习分割模型输出掩码图,用轮廓定位前景物体位置。
第 1 课 · 查找轮廓(findContours + drawContours)
概念
- 轮廓 :把图像边界上具有相同强度的点连接起来的曲线
- 每个轮廓是一个条目,是对象边界点的集合(以 x,y 坐标表示)
- 用于:形状分析、物体大小、定位前景物体
流程
BGR图像 → 转灰度 → Canny找二值图 → findContours → drawContours
函数签名
python
contours, hierarchy = cv2.findContours(image, mode, method)
cv2.drawContours(image, contours, contourIdx, color, thickness, lineType)
| findContours 参数 | 含义 |
|---|---|
image |
输入二值图像(需 BGR→灰度→Canny 得到) |
mode |
轮廓层级关系:RETR_LIST(全部)/ RETR_EXTERNAL(仅外轮廓)/ RETR_TREE |
method |
存储方式:CHAIN_APPROX_NONE(存全部点)/ CHAIN_APPROX_SIMPLE(压缩冗余点省内存) |
| drawContours 参数 | 含义 |
|---|---|
contourIdx |
-1 画全部;0 画第一个 |
color |
颜色(BGR) |
thickness |
线宽 |
完整代码(find_contour.py)
python
import cv2
image_path = "../data/balloon.jpg"
image = cv2.imread(image_path)
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image_binary = cv2.Canny(image_gray, 100, 200) # 二值图
contours, hierarchy = cv2.findContours(image_binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_NONE)
image_contours = cv2.drawContours(image, contours, -1,
(255, 0, 0), 2, cv2.LINE_AA)
cv2.imshow("image", image_contours)
cv2.waitKey(0)
cv2.destroyAllWindows()
流程示意
#mermaid-svg-77aA9sKj60oXxWuI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-77aA9sKj60oXxWuI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-77aA9sKj60oXxWuI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-77aA9sKj60oXxWuI .error-icon{fill:#552222;}#mermaid-svg-77aA9sKj60oXxWuI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-77aA9sKj60oXxWuI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-77aA9sKj60oXxWuI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-77aA9sKj60oXxWuI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-77aA9sKj60oXxWuI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-77aA9sKj60oXxWuI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-77aA9sKj60oXxWuI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-77aA9sKj60oXxWuI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-77aA9sKj60oXxWuI .marker.cross{stroke:#333333;}#mermaid-svg-77aA9sKj60oXxWuI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-77aA9sKj60oXxWuI p{margin:0;}#mermaid-svg-77aA9sKj60oXxWuI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-77aA9sKj60oXxWuI .cluster-label text{fill:#333;}#mermaid-svg-77aA9sKj60oXxWuI .cluster-label span{color:#333;}#mermaid-svg-77aA9sKj60oXxWuI .cluster-label span p{background-color:transparent;}#mermaid-svg-77aA9sKj60oXxWuI .label text,#mermaid-svg-77aA9sKj60oXxWuI span{fill:#333;color:#333;}#mermaid-svg-77aA9sKj60oXxWuI .node rect,#mermaid-svg-77aA9sKj60oXxWuI .node circle,#mermaid-svg-77aA9sKj60oXxWuI .node ellipse,#mermaid-svg-77aA9sKj60oXxWuI .node polygon,#mermaid-svg-77aA9sKj60oXxWuI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-77aA9sKj60oXxWuI .rough-node .label text,#mermaid-svg-77aA9sKj60oXxWuI .node .label text,#mermaid-svg-77aA9sKj60oXxWuI .image-shape .label,#mermaid-svg-77aA9sKj60oXxWuI .icon-shape .label{text-anchor:middle;}#mermaid-svg-77aA9sKj60oXxWuI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-77aA9sKj60oXxWuI .rough-node .label,#mermaid-svg-77aA9sKj60oXxWuI .node .label,#mermaid-svg-77aA9sKj60oXxWuI .image-shape .label,#mermaid-svg-77aA9sKj60oXxWuI .icon-shape .label{text-align:center;}#mermaid-svg-77aA9sKj60oXxWuI .node.clickable{cursor:pointer;}#mermaid-svg-77aA9sKj60oXxWuI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-77aA9sKj60oXxWuI .arrowheadPath{fill:#333333;}#mermaid-svg-77aA9sKj60oXxWuI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-77aA9sKj60oXxWuI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-77aA9sKj60oXxWuI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-77aA9sKj60oXxWuI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-77aA9sKj60oXxWuI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-77aA9sKj60oXxWuI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-77aA9sKj60oXxWuI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-77aA9sKj60oXxWuI .cluster text{fill:#333;}#mermaid-svg-77aA9sKj60oXxWuI .cluster span{color:#333;}#mermaid-svg-77aA9sKj60oXxWuI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-77aA9sKj60oXxWuI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-77aA9sKj60oXxWuI rect.text{fill:none;stroke-width:0;}#mermaid-svg-77aA9sKj60oXxWuI .icon-shape,#mermaid-svg-77aA9sKj60oXxWuI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-77aA9sKj60oXxWuI .icon-shape p,#mermaid-svg-77aA9sKj60oXxWuI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-77aA9sKj60oXxWuI .icon-shape .label rect,#mermaid-svg-77aA9sKj60oXxWuI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-77aA9sKj60oXxWuI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-77aA9sKj60oXxWuI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-77aA9sKj60oXxWuI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} drawContours
findContours
转二值
BGR 图像
彩色图
cvtColor 转灰度
Canny 找边缘(二值图)
按 mode/method 提取轮廓
返回 contours + hierarchy
把轮廓画回图像
-1=全部, 0=单个
真实演示图(蓝色图形 + 绿色轮廓):
| 二值形状图 | 绘制轮廓 |
|---|---|
![]() |
![]() |
轮廓把圆形边界用绿色描出,即可定位物体位置。
第 2 课 · 面积与周长(contourArea + arcLength)
概念
- 轮廓有属性:面积 和 周长
- 用于按大小/长度筛选轮廓(过滤太小的噪点)
函数签名
python
area = cv2.contourArea(contour, oriented=False)
perimeter = cv2.arcLength(curve, closed=True)
| 参数 | 含义 |
|---|---|
contour |
要计算面积的轮廓 |
oriented |
True 按方向(顺/逆时针)返回带符号面积;False 返回绝对值 |
curve |
轮廓 |
closed |
True=闭合轮廓,False=开放轮廓 |
完整代码(contour_properties.py)
python
import cv2
# ... 前面读图 + Canny + findContours,取 contours[0] ...
area = cv2.contourArea(contours[0], False)
print("contour area of 0 is", area)
perimeter = cv2.arcLength(contours[0], True)
print("contour perimeter of 0 is", perimeter)
要点
- 有多个轮廓时,改
contours[i]索引即可逐个算 - 可用
cv2.putText把面积/周长画到图像上
第 3 课 · 拟合形状(boundingRect + minEnclosingCircle)
概念
- 形状拟合 :找一个形状把所有边界点紧密包含在内
- 用于分析物体形状(如最小矩形、最小外接圆)
函数签名
python
x, y, w, h = cv2.boundingRect(contour) # 最小外接矩形
(x, y), radius = cv2.minEnclosingCircle(contour) # 最小外接圆
| 含义 |
|---|
x, y = 矩形左上角坐标 |
w, h = 宽和高(右下角 = (x+w, y+h)) |
(x, y) = 圆心坐标,radius = 半径 |
完整代码(fitting_shape.py)
python
import cv2
# ... 前面读图 + Canny + findContours ...
# 最小外接矩形
x, y, w, h = cv2.boundingRect(contours[0])
image = cv2.rectangle(image, (x, y), (x+w, y+h),
(0, 255, 0), 2, cv2.LINE_AA)
# 最小外接圆(需转整数)
(cx, cy), radius = cv2.minEnclosingCircle(contours[0])
image = cv2.circle(image, (int(cx), int(cy)), int(radius),
(0, 255, 0), 2, cv2.LINE_AA)
要点
boundingRect返回x,y,w,h(左上角 + 宽高);右下角 =x+w, y+hminEnclosingCircle返回圆心 + 半径- 圆心/半径可能是浮点,画图前要
int()
拟合示意
#mermaid-svg-aPrdtuzZOiZrwgvT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aPrdtuzZOiZrwgvT .error-icon{fill:#552222;}#mermaid-svg-aPrdtuzZOiZrwgvT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aPrdtuzZOiZrwgvT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aPrdtuzZOiZrwgvT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aPrdtuzZOiZrwgvT .marker.cross{stroke:#333333;}#mermaid-svg-aPrdtuzZOiZrwgvT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aPrdtuzZOiZrwgvT p{margin:0;}#mermaid-svg-aPrdtuzZOiZrwgvT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster-label text{fill:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster-label span{color:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster-label span p{background-color:transparent;}#mermaid-svg-aPrdtuzZOiZrwgvT .label text,#mermaid-svg-aPrdtuzZOiZrwgvT span{fill:#333;color:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT .node rect,#mermaid-svg-aPrdtuzZOiZrwgvT .node circle,#mermaid-svg-aPrdtuzZOiZrwgvT .node ellipse,#mermaid-svg-aPrdtuzZOiZrwgvT .node polygon,#mermaid-svg-aPrdtuzZOiZrwgvT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aPrdtuzZOiZrwgvT .rough-node .label text,#mermaid-svg-aPrdtuzZOiZrwgvT .node .label text,#mermaid-svg-aPrdtuzZOiZrwgvT .image-shape .label,#mermaid-svg-aPrdtuzZOiZrwgvT .icon-shape .label{text-anchor:middle;}#mermaid-svg-aPrdtuzZOiZrwgvT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aPrdtuzZOiZrwgvT .rough-node .label,#mermaid-svg-aPrdtuzZOiZrwgvT .node .label,#mermaid-svg-aPrdtuzZOiZrwgvT .image-shape .label,#mermaid-svg-aPrdtuzZOiZrwgvT .icon-shape .label{text-align:center;}#mermaid-svg-aPrdtuzZOiZrwgvT .node.clickable{cursor:pointer;}#mermaid-svg-aPrdtuzZOiZrwgvT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aPrdtuzZOiZrwgvT .arrowheadPath{fill:#333333;}#mermaid-svg-aPrdtuzZOiZrwgvT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aPrdtuzZOiZrwgvT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aPrdtuzZOiZrwgvT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aPrdtuzZOiZrwgvT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aPrdtuzZOiZrwgvT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aPrdtuzZOiZrwgvT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster text{fill:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT .cluster span{color:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aPrdtuzZOiZrwgvT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aPrdtuzZOiZrwgvT rect.text{fill:none;stroke-width:0;}#mermaid-svg-aPrdtuzZOiZrwgvT .icon-shape,#mermaid-svg-aPrdtuzZOiZrwgvT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aPrdtuzZOiZrwgvT .icon-shape p,#mermaid-svg-aPrdtuzZOiZrwgvT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aPrdtuzZOiZrwgvT .icon-shape .label rect,#mermaid-svg-aPrdtuzZOiZrwgvT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aPrdtuzZOiZrwgvT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aPrdtuzZOiZrwgvT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aPrdtuzZOiZrwgvT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 形状拟合
轮廓边界点
一组 (x,y) 点
boundingRect → 最小外接矩形
minEnclosingCircle → 最小外接圆
绿色矩形框住形状
绿色圆框住形状
真实演示图:
| 轮廓 | 矩形拟合 | 圆拟合 |
|---|---|---|
![]() |
![]() |
![]() |
矩形 / 圆把圆形的边界点紧密框住。
第 4 课 · 点在多边形内(pointPolygonTest)
概念
- 判断一个点相对轮廓的位置:内部 / 外部 / 边上(点在多边形内测试)
- 返回:
+1内部、-1外部、0在边界上
函数签名
python
result = cv2.pointPolygonTest(contour, pt, measureDist)
| 参数 | 含义 |
|---|---|
contour |
输入轮廓 |
pt |
待测试的点 (x, y) |
measureDist |
True=返回点到轮廓最短距离(带符号);False=仅判断内/外/边 |
完整代码(point_check.py)
python
import cv2
# ... 前面读图 + Canny + findContours ...
point = (100, 100)
result = cv2.pointPolygonTest(contours[0], point, False)
print("point result:", result) # +1/-1/0
# 画点看位置
image = cv2.circle(image, point, 3, (0, 255, 0), 3, cv2.LINE_AA)
三种结果的判定
#mermaid-svg-x44zUmm0qBYybSHn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-x44zUmm0qBYybSHn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x44zUmm0qBYybSHn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x44zUmm0qBYybSHn .error-icon{fill:#552222;}#mermaid-svg-x44zUmm0qBYybSHn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x44zUmm0qBYybSHn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x44zUmm0qBYybSHn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x44zUmm0qBYybSHn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x44zUmm0qBYybSHn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x44zUmm0qBYybSHn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x44zUmm0qBYybSHn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x44zUmm0qBYybSHn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x44zUmm0qBYybSHn .marker.cross{stroke:#333333;}#mermaid-svg-x44zUmm0qBYybSHn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x44zUmm0qBYybSHn p{margin:0;}#mermaid-svg-x44zUmm0qBYybSHn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x44zUmm0qBYybSHn .cluster-label text{fill:#333;}#mermaid-svg-x44zUmm0qBYybSHn .cluster-label span{color:#333;}#mermaid-svg-x44zUmm0qBYybSHn .cluster-label span p{background-color:transparent;}#mermaid-svg-x44zUmm0qBYybSHn .label text,#mermaid-svg-x44zUmm0qBYybSHn span{fill:#333;color:#333;}#mermaid-svg-x44zUmm0qBYybSHn .node rect,#mermaid-svg-x44zUmm0qBYybSHn .node circle,#mermaid-svg-x44zUmm0qBYybSHn .node ellipse,#mermaid-svg-x44zUmm0qBYybSHn .node polygon,#mermaid-svg-x44zUmm0qBYybSHn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x44zUmm0qBYybSHn .rough-node .label text,#mermaid-svg-x44zUmm0qBYybSHn .node .label text,#mermaid-svg-x44zUmm0qBYybSHn .image-shape .label,#mermaid-svg-x44zUmm0qBYybSHn .icon-shape .label{text-anchor:middle;}#mermaid-svg-x44zUmm0qBYybSHn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x44zUmm0qBYybSHn .rough-node .label,#mermaid-svg-x44zUmm0qBYybSHn .node .label,#mermaid-svg-x44zUmm0qBYybSHn .image-shape .label,#mermaid-svg-x44zUmm0qBYybSHn .icon-shape .label{text-align:center;}#mermaid-svg-x44zUmm0qBYybSHn .node.clickable{cursor:pointer;}#mermaid-svg-x44zUmm0qBYybSHn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x44zUmm0qBYybSHn .arrowheadPath{fill:#333333;}#mermaid-svg-x44zUmm0qBYybSHn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x44zUmm0qBYybSHn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x44zUmm0qBYybSHn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x44zUmm0qBYybSHn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x44zUmm0qBYybSHn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x44zUmm0qBYybSHn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x44zUmm0qBYybSHn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x44zUmm0qBYybSHn .cluster text{fill:#333;}#mermaid-svg-x44zUmm0qBYybSHn .cluster span{color:#333;}#mermaid-svg-x44zUmm0qBYybSHn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-x44zUmm0qBYybSHn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x44zUmm0qBYybSHn rect.text{fill:none;stroke-width:0;}#mermaid-svg-x44zUmm0qBYybSHn .icon-shape,#mermaid-svg-x44zUmm0qBYybSHn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x44zUmm0qBYybSHn .icon-shape p,#mermaid-svg-x44zUmm0qBYybSHn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x44zUmm0qBYybSHn .icon-shape .label rect,#mermaid-svg-x44zUmm0qBYybSHn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x44zUmm0qBYybSHn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x44zUmm0qBYybSHn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x44zUmm0qBYybSHn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 三种点位
轮廓
闭合边界
点A:在内部
+1
点B:在边上
0
点C:在外部
-1
真实演示图(圆心红点在内部 / 黄点在外部 / 绿点在边上):
| 点测试结果 |
|---|
![]() |
红点(内)=+1,绿点(边上)=0,黄点(外)=-1。
本章小结(速查表)
| 操作 | 函数 | 关键点 |
|---|---|---|
| 找轮廓 | cv2.findContours(img, mode, method) |
输入需二值图;返回(contours, hierarchy) |
| 画轮廓 | cv2.drawContours(img, contours, idx, color, t) |
idx=-1 全部 |
| 面积 | cv2.contourArea(contour, oriented) |
oriented=False 返回绝对值 |
| 周长 | cv2.arcLength(curve, closed) |
closed=True 闭合 |
| 外接矩形 | cv2.boundingRect(contour) |
返回 x,y,w,h |
| 外接圆 | cv2.minEnclosingCircle(contour) |
返回 (cx,cy), r;画前 int() |
| 点测试 | cv2.pointPolygonTest(contour, pt, measureDist) |
+1内 / 0边 / -1外 |
核心易错点
- findContours 的输入必须是二值图(BGR→灰度→Canny)
- Canny 作二值图是找轮廓的常见前置步骤
findContours返回 两个值 :(contours, hierarchy)boundingRect是x,y,w,h(不是两对角点),右下角 =x+w, y+h- 圆心/半径浮点要
int()才能画 pointPolygonTest返回 +1 / 0 / -1 ,measureDist=False只判位置




