OpenCV 轮廓与属性:查找轮廓、面积周长、形状拟合与点测试

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+h
  • minEnclosingCircle 返回圆心 + 半径
  • 圆心/半径可能是浮点,画图前要 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外

核心易错点

  1. findContours 的输入必须是二值图(BGR→灰度→Canny)
  2. Canny 作二值图是找轮廓的常见前置步骤
  3. findContours 返回 两个值(contours, hierarchy)
  4. boundingRectx,y,w,h(不是两对角点),右下角 = x+w, y+h
  5. 圆心/半径浮点要 int() 才能画
  6. pointPolygonTest 返回 +1 / 0 / -1measureDist=False 只判位置
相关推荐
asdzx671 小时前
使用 Python 为 Excel 添加各类数据验证规则
开发语言·python·excel
AI_AGENT_DEV_AI1 小时前
AI 英语教培软件系统的开发费用
人工智能
叠层归一研究院1 小时前
缝合维度:时空3+1结构的拓扑起源
人工智能·算法·机器学习·agi
梦梦代码精1 小时前
深度体验BuildingAI:一款集成商业闭环的开源智能体平台
人工智能·docker·开源·代码规范
秋名山码民1 小时前
拙见AI——AI操作系统
数据库·人工智能
得物技术1 小时前
企业级 MultiAgent 的记忆系统:短期上下文与四层记忆架构实现|得物技术
java·人工智能·后端
vivo互联网技术1 小时前
vivo 广告小游戏:从"写代码"到"说需求"
前端·人工智能·游戏开发
动物园猫1 小时前
考试作弊行为目标检测数据集:3类别、5,700张图像 | 目标检测
人工智能·目标检测·目标跟踪