上一篇我们学习了水印添加与去除技巧,今天我们介绍一个升级场景——物体尺寸测量。无论是工业检测中的小型零件测量,还是日常中的文具、首饰测量,用OpenCV结合参考物,实现测量。

工业检测: 小型零件(如螺丝、垫片、电子元件)的尺寸检测,判断零件是否符合规格;
日常测量: 文具、首饰、小型生活用品的尺寸测量,拍摄图像,快速获取尺寸;
电商场景: 商品尺寸标注,用于商品详情页标注。
物体尺寸测量的主要逻辑的是「像素比例换算」:
准备参考物: 放置一个已知实际尺寸的参考物(如1元硬币,直径25mm;尺子,宽度10mm),与目标物体同屏拍摄;
计算像素比例: 通过轮廓分析,提取参考物的像素尺寸(如硬币的像素直径),计算出“1mm对应多少像素”(像素比例=参考物像素尺寸÷参考物实际尺寸);
测量目标物体: 提取目标物体的像素大小(长宽),通过像素比例换算为实际尺寸(实际尺寸=目标物体像素尺寸÷像素比例)。
图像预处理: 高斯滤波去噪、灰度化、二值化,提升轮廓提取的准确性;
轮廓提取: 提取参考物和目标物体的轮廓,筛选有效轮廓;
几何形状分析: cv2.minEnclosingCircle()、cv2.boundingRect()函数,拟合参考物和目标物体的形状,获取像素大小。
import cv2
import numpy as np
def measure_object_size(
img_path: str,
ref_size: float = 25,
ref_shape: str = "circle"
):
"""
物体尺寸测量函数
:param img_path: 包含参考物和目标物体的图像路径
:param ref_size: 参考物实际尺寸(mm),默认硬币直径25mm
:param ref_shape: 参考物形状(circle/rectangle),默认圆形
"""
# 1. 图像预处理
img = cv2.imread(img_path)
img_copy = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯滤波去噪
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# 二值化(自适应阈值)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 去除细小噪声
kernel = np.ones((3, 3), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
# 2. 提取轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 按纵坐标排序轮廓,让参考物位置在上方
contours = sorted(contours, key=lambda c: cv2.boundingRect(c)[1])
contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 1000]
# 3. 计算像素比例(像素/mm)
ref_pixel_size = 0
for cnt in contours:
# 拟合参考物形状
if ref_shape == "circle":
# 圆形拟合
(x, y), radius = cv2.minEnclosingCircle(cnt)
ref_pixel_size = 2 * radius
cv2.circle(img_copy, (int(x), int(y)), int(radius), (0, 255, 0), 2)
cv2.putText(
img_copy,
f"Radius: {ref_size}mm", (int(x)-50, int(y)-20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2
)
break
elif ref_shape == "rectangle":
# 矩形拟合
x, y, w, h = cv2.boundingRect(cnt)
ref_pixel_size = w
cv2.rectangle(img_copy, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(img_copy, f"Width: {ref_size}mm", (x, y-20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
break
# 计算像素比例(1mm对应多少像素)
pixel_per_mm = ref_pixel_size / ref_size
# 4. 测量目标物体尺寸
for i, cnt in enumerate(contours[1:]): # 跳过参考物轮廓
# 拟合矩形,获取像素宽度和高度
x, y, w, h = cv2.boundingRect(cnt)
# 换算为实际尺寸(mm),保留1位小数
actual_w = round(w / pixel_per_mm, 1)
actual_h = round(h / pixel_per_mm, 1)
# 绘制目标物体轮廓和尺寸
cv2.rectangle(img_copy, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(img_copy, f"Size: {actual_w}mm x {actual_h}mm", (x, y-20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
# 5. 显示结果
cv2.imshow("Result", img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 调用函数,测量物体尺寸(参考物为硬币,直径25mm)
measure_object_size(
img_path="object_measure.png", # 包含硬币和目标物体的图像
ref_size=25,
ref_shape="circle"
)

(从实验结果可以看出,计算结果会受到光线干扰的影响,例如下方矩形的光线阴影问题,导致其纵向的宽度计算并不是很精准)
测量误差大: 确保参考物和目标物体在同一平面,拍摄时保持镜头垂直于物体,避免倾斜;参考物尽量靠近目标物体,减少光线干扰;
难以提取轮廓: 调整二值化阈值,或增加形态学操作的迭代次数,确保轮廓清晰;
参考物识别错误: 确保参考物是图像中最明显的,或手动指定参考物轮廓;
尺寸显示异常: 检查参考物实际尺寸是否正确。
本篇我们掌握了物体尺寸测量的技巧,基于像素比例换算,结合参考物,实现了物体大小的测量。
下篇我们讲解本期最后一个OpenCV使用场景——视频人脸模糊。
往期回顾: