OpenCV-Python Tutorial : A Candy from Official Main Page(二)
三、image processing
3.8图像金字塔-cv.pyrDown、cv.pyrUp
图像金字塔有两种,一种是高斯金字塔,一种是拉普拉斯金字塔。
3.8.1高斯金字塔
高斯金字塔是一种多分辨率图像表示方法,通过对图像逐层降采样(缩小)生成一系列分辨率递减的图像集合。其核心思想是模拟人眼观察物体时从粗到细的感知过程,广泛应用于图像缩放、特征提取、图像融合等任务。
3.8.2拉普拉斯金字塔
拉普拉斯金字塔是一种基于高斯金字塔的多尺度图像表示方法,通过差分操作提取不同分辨率下的高频细节信息,常用于图像融合、压缩和增强等任务。其核心思想是存储高斯金字塔相邻层之间的丢失信息,从而实现图像的无损重建。
cv.pyrDown()
对图像进行高斯平滑(模糊)并降采样(缩小到 1/4 大小,宽高各减半)
dst = cv.pyrDown(src[, dst[, dstsize[, borderType]]])
src:输入图像(单通道或多通道)。
dst(可选):输出图像。
dstsize(可选):目标尺寸,默认自动计算为 (src.cols/2, src.rows/2)。
borderType(可选):边界填充方式(默认 cv.BORDER_DEFAULT)。
cv.pyrUp()
对图像进行上采样(放大到 2 倍宽高)并高斯平滑,但不会恢复降采样时丢失的信息。
dst = cv.pyrUp(src[, dst[, dstsize[, borderType]]])
参数:
(同 cv.pyrDown,但 dstsize 默认为 (src.cols*2, src.rows*2))
cv.subtract()
计算两幅图像的差值(逐像素相减),用于提取拉普拉斯金字塔层。
dst = cv.subtract(src1, src2[, dst[, mask[, dtype]]])
src1, src2:输入图像(需同尺寸、同通道数)。
mask(可选):掩膜,仅处理指定区域。
dtype(可选):输出数据类型。
demo1:对比原图与cv.pyrdown下采样与cv.pyrup上采样的结果
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltimg = cv.imread('image1.png')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
assert img is not None, "file could not be read, check with os.path.exists()"
lower_reso = cv.pyrDown(img)
higher_reso = cv.pyrUp(lower_reso)titles = ['img', 'lower_reso', 'higher_reso']
images = [img, lower_reso, higher_reso]for i in range(3):plt.subplot(1, 3, i+1)plt.imshow(images[i])plt.title(titles[i])plt.xticks([])plt.yticks([])
plt.show()
demo2:可视化图像金字塔
import cv2
import numpy as np
from matplotlib import pyplot as pltdef build_gaussian_pyramid(img, levels):g = img.copy()# 确保初始尺寸是偶数if g.shape[0] % 2 != 0 or g.shape[1] % 2 != 0:g = cv2.resize(g, (g.shape[1] // 2 * 2, g.shape[0] // 2 * 2))pyramid = [g]for _ in range(levels):g = cv2.pyrDown(g)pyramid.append(g)return pyramiddef build_laplacian_pyramid(gaussian_pyramid):pyramid = [gaussian_pyramid[-1]]for i in range(len(gaussian_pyramid)-1, 0, -1):up_img = cv2.pyrUp(gaussian_pyramid[i])# 确保尺寸匹配h, w = gaussian_pyramid[i-1].shape[:2]up_img = cv2.resize(up_img, (w, h))err = cv2.subtract(gaussian_pyramid[i-1], up_img)pyramid.append(err)return pyramid[::-1]# 读取图像并翻转
img1 = cv2.imread('image3.png')
img1 = img1[:,:1200]
cv2.imshow("",img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
img2 = cv2.flip(img1, 1)# 构建金字塔
gp1 = build_gaussian_pyramid(img1, 5) # 注意:6层高斯金字塔实际生成7个图像(含原图)
gp2 = build_gaussian_pyramid(img2, 5)
lp1 = build_laplacian_pyramid(gp1)
lp2 = build_laplacian_pyramid(gp2)# 绘制子图
plt.figure(figsize=(15, 10))
for i in range(6):plt.subplot(4, 6, i+1)plt.imshow(cv2.cvtColor(gp1[i], cv2.COLOR_BGR2RGB))plt.title(f'GP1 L{i}')plt.axis('off')
for i in range(6):plt.subplot(4, 6, i+7)plt.imshow(cv2.cvtColor(gp2[i], cv2.COLOR_BGR2RGB))plt.title(f'GP2 L{i}')plt.axis('off')
for i in range(6):plt.subplot(4, 6, i+13)plt.imshow(cv2.cvtColor(lp1[i], cv2.COLOR_BGR2RGB))plt.title(f'LP1 L{i}')plt.axis('off')
for i in range(6):plt.subplot(4, 6, i+19)plt.imshow(cv2.cvtColor(lp2[i], cv2.COLOR_BGR2RGB))plt.title(f'LP2 L{i}')plt.axis('off')
plt.tight_layout()
plt.show()
如图,img1和img2的高斯金字塔和拉普拉斯金字塔,以img1为例:
img1的高斯金字塔是第一行,拉普拉斯金字塔是第三行,然后高斯金字塔和拉普拉斯金字塔的最顶层L5是一样的,拉普拉斯的L4 = 高斯的L4 - cv.pyrup(高斯的L5)
3.9 轮廓(contours in opencv)
3.9.1基础介绍-cv.findContours()、
cv.drawContours()
轮廓可以简单地解释为连接所有具有相同颜色或强度的连续点(沿边界)的曲线。轮廓是用于形状分析、物体检测与识别的有用工具。
为提高准确性,请使用二值图像。因此在查找轮廓前,应先应用阈值处理或Canny边缘检测。
从OpenCV 3.2版本开始,findContours()函数不再修改源图像。
在OpenCV中,查找轮廓就像从黑色背景中寻找白色物体。所以请记住:待检测物体应为白色,背景应为黑色。
cv.findContours()
是 OpenCV 中用于检测图像轮廓的函数
contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
参数说明
image – 输入图像(必须为 二值图像,通常需要先进行阈值处理或边缘检测)。
注意:OpenCV 3.2 之后,该函数不会修改原始图像。
目标物体应为 白色(前景),背景应为 黑色。
mode – 轮廓检索模式,决定如何提取轮廓:
cv.RETR_EXTERNAL – 只检测最外层轮廓
cv.RETR_LIST – 检测所有轮廓,不建立层级关系
cv.RETR_TREE – 检测所有轮廓,并建立完整的层级结构(嵌套轮廓)
method – 轮廓近似方法:
cv.CHAIN_APPROX_NONE – 存储所有轮廓点(不压缩)
cv.CHAIN_APPROX_SIMPLE – 压缩冗余点(如直线只保留端点)返回值
contours – 检测到的轮廓列表,每个轮廓是一个点集(np.array)。
hierarchy – 轮廓的层级信息(用于嵌套轮廓分析)。
绘制轮廓
cv.drawContours()
函数用于绘制轮廓,也可以用来绘制任何已知边界点的形状
cv.drawContours(image, # 要绘制轮廓的目标图像(通常先复制原图)contours, # 轮廓列表(Python list格式)contourIdx, # 要绘制的轮廓索引(-1表示绘制所有轮廓)color, # 轮廓颜色(BGR格式,如 (0, 255, 0) 代表绿色)thickness, # 轮廓线粗细(像素值,-1表示填充轮廓内部)[lineType], # (可选)线型(如 cv.LINE_AA抗锯齿)[hierarchy], # (可选)层级关系(配合复杂轮廓结构使用)[maxLevel], # (可选)最大绘制层级(默认全部绘制)[offset] # (可选)轮廓点坐标偏移量
)
关键参数
image
目标图像:必须是 彩色图像(如果是灰度图需先转BGR,否则无法显示颜色)。
contours
轮廓列表:直接传入 cv.findContours() 返回的 contours 列表。
注意:即使只画一个轮廓,也要以列表形式传入,如 [contour]。
contourIdx
轮廓索引:
-1:绘制所有轮廓。
0:只绘制第一个轮廓。
1:只绘制第二个轮廓,依此类推。
color 和 thickness
颜色:BGR格式,例如 (255, 0, 0) 表示蓝色。
线粗:
2:2像素宽的线条。
-1:填充轮廓内部(如绘制实心形状)。
demo:画出图片轮廓
from pickle import NONE
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltimg = cv.imread("image1.png")
assert img is not None, "img is not exists"
# img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
img_edge = cv.Canny(img_gray,100,200)contours, hierarchy = cv.findContours(img_edge, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(img, contours, -1, (0,125,0), 2)
cv.imshow('All Contours', img)
cv.waitKey(0)
轮廓实际上是一层包含一层的numpy数组
3.9.2Contour Features基础特征提取(轮廓分析基础)
- 常用特征(面积、周长、质心、边界框等)
- 轮廓近似(多边形拟合)
- 凸包(Convex Hull)
- 几何形状检查(矩形度、圆形度等)
1.矩(Moments)-cv.moments(contour)
moments = cv.moments(contour)
cx = int(moments["m10"] / moments["m00"]) # 质心x坐标
cy = int(moments["m01"] / moments["m00"]) # 质心y坐标
area = moments["m00"] # 轮廓面积
2.面积与周长-cv.contourArea(contour)、cv.arcLength(contour, closed=True)
area = cv.contourArea(contour) # 轮廓面积
perimeter = cv.arcLength(contour, closed=True) # 周长(closed表示轮廓是否闭合)
3.边框cv.boundingRect(contour)、cv.rectangle、cv.minAreaRect(contour)、cv.boxPoints
矩形
x, y, w, h = cv.boundingRect(contour) # 矩形框坐标和宽高
cv.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
旋转矩形
rect = cv.minAreaRect(contour) # 返回旋转矩形(中心点、宽高、旋转角度)
box = cv.boxPoints(rect) # 获取四个顶点坐标
box = np.int0(box) # 转为整数
cv.drawContours(img, [box], 0, (0,0,255), 2)
4.轮廓近似-cv.arcLength、cv.approxPolyDP
用更少的点简化轮廓(减少噪声或压缩数据)
epsilon = 0.01 * cv.arcLength(contour, True) # 近似精度(1%周长)
approx = cv.approxPolyDP(contour, epsilon, True) # 多边形近似
应用:检测图像中的多边形(如识别四边形、三角形)。
5.凸包(Convex Hull)-cv.convexHull
计算轮廓的凸包(包裹轮廓的最小凸多边形)
hull = cv.convexHull(contour) # 计算凸包
cv.drawContours(img, [hull], 0, (255,0,0), 2)
应用:检测凸性缺陷或物体形状分析。
6.几何形状检查-cv.isContourConvex、cv.minEnclosingCircle
(1) 凸性检测
is_convex = cv.isContourConvex(contour) # 返回True/False
(2) 矩形度与圆形度
矩形度:轮廓面积与最小外接矩形面积的比值。
rect_area = w * h
extent = area / rect_area # 接近1则为矩形
圆形度:衡量轮廓接近圆形的程度。
(x,y), radius = cv.minEnclosingCircle(contour)
circle_area = np.pi * radius**2
circularity = area / circle_area # 接近1则为圆形
7 .关键应用场景
- 目标检测:通过边界框定位物体。
- 形状分类:利用面积、周长、Hu矩区分不同形状。
- 工业检测:检查零件是否符合几何规格(如凸性缺陷)。
- OCR预处理:用多边形近似提取文字区域。
3.9.3提取高级几何属性
包括:
- 纵横比(Aspect Ratio)
- 轮廓面积与凸包面积比(Solidity)
- 等效直径(Equivalent Diameter)
- 方向(Orientation)
- 掩膜与像素点(Mask & Pixel Points)
- 最大值/最小值及其位置
- 平均颜色/强度
- 极点(Extreme Points)
1.纵横比
边界矩形宽高比(width/height
)
x, y, w, h = cv.boundingRect(contour)
aspect_ratio = float(w) / h
应用:区分细长物体(如笔)与方形物体(如硬币)。
2.轮廓面积与凸包面积比(Solidity)
轮廓面积(contourArea
)与凸包面积(convexHull
)的比值。
hull = cv.convexHull(contour)
hull_area = cv.contourArea(hull)
solidity = float(area) / hull_area # area为轮廓面积
应用:检测凹性缺陷(如零件损伤)。
3.等效直径(Equivalent Diameter)
与轮廓面积相同的圆的直径。
equi_diameter = np.sqrt(4 * area / np.pi)
4.方向(Orientation)
轮廓的主轴角度(通过拟合椭圆获取)。
(x, y), (MA, ma), angle = cv.fitEllipse(contour) # MA:长轴, ma:短轴, angle:角度
5.生成轮廓掩膜(Mask)
mask = np.zeros(gray.shape, np.uint8)
cv.drawContours(mask, [contour], 0, 255, -1) # -1表示填充内部
pixel_points = np.transpose(np.nonzero(mask)) # 获取轮廓内所有像素坐标
6.最大值/最小值及位置
在ROI内找到最亮/最暗点。
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(gray, mask=mask)
7.平均颜色/强度
mean_val = cv.mean(img, mask=mask) # 返回BGR通道均值
8.极点(Extreme Points)
轮廓的最上、最下、最左、最右点。
leftmost = tuple(contour[contour[:, :, 0].argmin()][0])
rightmost = tuple(contour[contour[:, :, 0].argmax()][0])
topmost = tuple(contour[contour[:, :, 1].argmin()][0])
bottommost = tuple(contour[contour[:, :, 1].argmax()][0])
应用:物体姿态估计或裁剪感兴趣区域。
9.属性速查表
属性 函数/公式 应用场景
纵横比 w / h 形状分类
Solidity area / hull_area 凹性检测
等效直径 sqrt(4*area/π) 尺寸筛选
方向 cv.fitEllipse() 物体朝向分析
极值点 argmin() / argmax() 边界定位
平均颜色 cv.mean() 区域色彩分析
3.9.4 凸性缺陷(Convexity Defects)-cv.convexHull、cv.convexityDefects
OpenCV: Contours : More Functions
-
凸包(Convex Hull):包裹轮廓的最小凸多边形(无凹陷)。
-
凸性缺陷:轮廓与凸包之间的偏离区域(即轮廓的凹陷部分)。
hull = cv.convexHull(contour, returnPoints=False) # 返回凸包的索引而非坐标点
defects = cv.convexityDefects(contour, hull) # 计算凸性缺陷returnPoints=False:要求 convexHull 返回凸包点在原始轮廓中的索引(而非坐标),这是计算缺陷的必要条件。
defects 返回值:一个形状为 (N,1,4) 的 NumPy 数组,每行包含 4 个值:
[start_index, end_index, farthest_index, distance]
start_point:缺陷起始点(轮廓索引)。
end_point:缺陷结束点(轮廓索引)。
farthest_point:凹陷最深处点(轮廓索引)。
distance:最远点到凸包的近似距离。
示例
import cv2 as cv
import numpy as npimg = cv.imread('star.jpg')
assert img is not None, "file could not be read, check with os.path.exists()"
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thresh = cv.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt = contours[0]hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)for i in range(defects.shape[0]):s,e,f,d = defects[i,0]start = tuple(cnt[s][0])end = tuple(cnt[e][0])far = tuple(cnt[f][0])cv.line(img,start,end,[0,255,0],2)cv.circle(img,far,5,[0,0,255],-1)cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()
应用
手势识别
通过手指间的凸性缺陷数量判断伸出的手指数。
工业检测
检测零件表面的凹痕或缺陷(如齿轮齿槽的完整性)。
医学图像分析
识别器官轮廓的异常凹陷(如心脏瓣膜形态)。
3.9.5匹配形状-cv.matchShapes()
OpenCV附带一个函数cv.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回一个显示相似性的指标。结果越低,匹配度越好。它是根据hu-moment值计算的。
import cv2 as cv
import numpy as npimg1 = cv.imread('star.jpg', cv.IMREAD_GRAYSCALE)
img2 = cv.imread('star2.jpg', cv.IMREAD_GRAYSCALE)
assert img1 is not None, "file could not be read, check with os.path.exists()"
assert img2 is not None, "file could not be read, check with os.path.exists()"ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )
3.9.6轮廓层级(Hierarchy)
主要内容包括:
1.轮廓层级(Hierarchy) 的本质与数据结构
2. 4 种轮廓检索模式(RETR_LIST
, RETR_EXTERNAL
, RETR_CCOMP
, RETR_TREE
)的区别
3.举例如何利用层级关系分析嵌套轮廓(如孔洞检测)