OpenCV图像测量:两种高效方法对比

📅 2026/7/21 19:37:58
OpenCV图像测量:两种高效方法对比
在计算机视觉领域图像测量是一项常见而重要的任务。本文将详细介绍两种基于OpenCV的图像测量方法并对它们的实现原理、优缺点及适用场景进行深入分析。方法一基于参考物体宽度的测量方法核心原理第一种方法通过设定已知宽度的参考物体通常放置在图像最左侧计算像素与实际尺寸的比例关系从而测量图像中其他物体的尺寸。代码实现分析# 导入必要的库 from scipy.spatial import distance as dist from imutils import perspective from imutils import contours import numpy as np import argparse import imutils import cv2 # 定义中点函数 def midpoint(ptA, ptB): return ((ptA[0] ptB[0]) * 0.5, (ptA[1] ptB[1]) * 0.5) # 设置内部参数 args { image: rD:\data\05.bmp, width: 25.0 # 参考物体的实际宽度 }以上代码段首先导入了必要的库并定义了中点计算函数和参数设置。# 图像预处理 img cv2.imread(args[image]) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV) # 膨胀操作增强轮廓 kernel np.ones((3, 3), np.uint8) thresh cv2.dilate(thresh, kernel, iterations1) # 填充图像四角以消除边缘干扰 height, width thresh.shape thresh[0:120, 0:160] 0 # 左上角 thresh[0:150, width - 180:] 0 # 右上角 thresh[height - 120:, 0:160] 0 # 左下角 thresh[height - 150:, width - 160:] 0 # 右下角预处理阶段包括读取图像、灰度转换、二值化和膨胀操作最后填充图像四角以消除边缘干扰。# 寻找物体轮廓并排序 cnts cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts imutils.grab_contours(cnts) (cnts, _) contours.sort_contours(cnts) # 初始化像素与实际尺寸的比例 pixelsPerMetric None # 遍历每个轮廓进行测量 for c in cnts: if cv2.contourArea(c) 10000: # 忽略面积过小的轮廓 continue # 计算最小外接矩形 box cv2.minAreaRect(c) box cv2.boxPoints(box) box np.array(box, dtypeint) box perspective.order_points(box) # 计算各边中点并绘制 (tl, tr, br, bl) box (tltrX, tltrY) midpoint(tl, tr) (blbrX, blbrY) midpoint(bl, br) (tlblX, tlblY) midpoint(tl, bl) (trbrX, trbrY) midpoint(tr, br) # 计算像素距离 dA dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) dB dist.euclidean((tlblX, tlblY), (trbrX, trbrY)) # 设置比例使用最左侧物体的已知宽度 if pixelsPerMetric is None: pixelsPerMetric dB / args[width] # 计算实际尺寸 dimA dA / pixelsPerMetric * 0.98 # 0.98为修正系数 dimB dB / pixelsPerMetric * 0.98 # 绘制测量结果 cv2.putText(orig, {:.3f}mm.format(dimA), (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 1) cv2.putText(orig, {:.3f}mm.format(dimB), (int(trbrX 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 1)测量阶段首先寻找并排序轮廓然后为每个轮廓计算最小外接矩形通过参考物体的宽度建立像素与实际尺寸的转换关系最后计算并显示实际尺寸。方法二基于硬币作为参考的测量方法核心原理第二种方法使用硬币作为参考物体已知实际直径通过检测图像中最大的圆形轮廓来识别硬币然后计算像素与实际尺寸的比例关系。代码实现分析import cv2 import numpy as np import matplotlib.pyplot as plt def find_and_measure_objects(image_path, coin_real_diameter_mm25.0): # 读取图像 img cv2.imread(image_path) if img is None: raise ValueError(图像加载失败请检查路径) # 转换为灰度图并二值化 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV) # 填充四角消除干扰 height, width thresh.shape thresh[0:120, 0:160] 0 # 左上角 thresh[0:140, width - 180:] 0 # 右上角 thresh[height - 120:, 0:160] 0 # 左下角 thresh[height - 150:, width - 160:] 0 # 右下角该方法封装在一个函数中首先进行图像读取、灰度转换、二值化和边缘处理。# 查找轮廓并找到最大的轮廓假设是硬币 contours, _ cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) max_contour max(contours, keycv2.contourArea) # 获取硬币的最小外接圆 (x, y), radius cv2.minEnclosingCircle(max_contour) center (int(x), int(y)) radius int(radius) diameter_px 2 * radius # 计算像素到毫米的转换比例 px_to_mm coin_real_diameter_mm / coin_diameter_px这部分代码查找轮廓并找到最大的轮廓假设为硬币然后计算硬币的最小外接圆基于已知的硬币实际直径建立像素与毫米的转换关系。# 遍历所有轮廓并测量 measurements [] for i, contour in enumerate(contours): area cv2.contourArea(contour) if area 20000 or contour is max_contour: # 忽略小物体和硬币本身 continue # 获取最小外接矩形 rect cv2.minAreaRect(contour) box cv2.boxPoints(rect) box np.int0(box) # 计算尺寸 edge1 np.linalg.norm(box[0] - box[1]) edge2 np.linalg.norm(box[1] - box[2]) width_px, height_px max(edge1, edge2), min(edge1, edge2) # 转换为真实尺寸 width_mm width_px * px_to_mm height_mm height_px * px_to_mm # 存储测量结果 measurements.append({ id: i 1, width_px: width_px, height_px: height_px, width_mm: width_mm, height_mm: height_mm })测量阶段遍历所有轮廓计算每个物体的最小外接矩形并根据像素与毫米的转换关系计算实际尺寸。# 使用matplotlib显示结果 plt.figure(figsize(15, 5)) plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.title(Original Image), plt.axis(off) plt.subplot(132), plt.imshow(thresh, cmapgray) plt.title(Threshold Image), plt.axis(off) plt.subplot(133), plt.imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)) plt.title(Measurement Results), plt.axis(off) plt.tight_layout() plt.show() # 打印测量结果表格 print(\n物体测量结果) print(f{ID:5}{宽度(mm):10}{高度(mm):10}{宽度(px):10}{高度(px):10}) print(- * 45) for m in measurements: print(f{m[id]:5}{m[width_mm]:10.3f}{m[height_mm]:10.13}{m[width_px]:10.3f}{m[height_px]:10.3f}) return result_img, measurements最后该方法使用matplotlib显示原始图像、二值化图像和测量结果并以表格形式打印测量数据。两种方法的对比分析1、相同点1. 都使用OpenCV进行图像处理和轮廓检测2. 都需要参考物体来建立像素与实际尺寸的转换关系3. 都采用二值化方法突出物体轮廓4. 都使用最小外接矩形来表示物体的尺寸5. 都填充图像四角以消除边缘干扰2、不同点对比项方法一方法二参考物体已知宽度的物体通常放在最左侧硬币已知直径代码结构线性执行结构函数封装结构轮廓排序从左到右排序无需排序通过面积识别参考物体显示方式使用 OpenCV 的 imshow使用 matplotlib 显示多图对比结果输出仅在图像上显示图像显示 表格数据输出适用场景有规则排列的物体测量包含圆形参考物的任意场景优缺点分析方法一优点实现简单直观对于规则排列的物体测量效率高无需复杂的轮廓识别算法方法一缺点依赖参考物体的位置必须在最左侧代码结构较为松散仅支持在图像上显示结果方法二优点封装性更好易于复用参考物体硬币放置位置更灵活结果可视化更丰富多图对比 表格数据代码结构更清晰方法二缺点依赖于硬币在图像中是最大的圆形物体当图像中有多个类似大小的圆形物体时可能误判实现相对复杂适用场景推荐方法一适用场景生产线检测中物体规则排列的场景需要快速实现简单测量的场景对代码封装要求不高的场景方法二适用场景通用物体测量场景需要美观结果展示的场景多次复用测量功能的场景有圆形参考物体如硬币的场景代码优化建议方法一优化建议1. 封装为函数def measure_objects_by_reference(image_path, ref_width_mm, min_area10000): # 将原代码封装为函数 # 返回测量结果和处理后的图像2. 增加异常处理try: img cv2.imread(image_path) if img is None: raise FileNotFoundError(f无法读取图像{image_path}) except Exception as e: print(f图像读取错误{e}) return None, None3. 增加参数可调性def measure_objects_by_reference(image_path, ref_width_mm, threshold_value120, min_area10000, correction_factor0.98): # 实现代码方法二优化建议1. 增加硬币检测的鲁棒性def find_coin_contour(contours): # 不仅仅依靠面积还可以结合圆形度等特征 coin_contour None max_circularity -1 for contour in contours: if cv2.contourArea(contour) 10000: # 忽略小轮廓 continue # 计算圆形度 (4π*面积/周长²) perimeter cv2.arcLength(contour, True) if perimeter 0: continue circularity 4 * np.pi * cv2.contourArea(contour) / (perimeter ** 2) # 选择圆形度最高且面积较大的轮廓作为硬币 if circularity max_circularity and circularity 0.8: # 圆形度阈值 max_circularity circularity coin_contour contour return coin_contour2. 增加图像预处理的灵活性def preprocess_image(img, threshold_value120, fill_cornersTrue, corner_fill_sizesNone): # 实现灵活的图像预处理3. 支持多种显示模式def display_results(img, thresh, result_img, display_modematplotlib): if display_mode opencv: cv2.imshow(Original, img) cv2.imshow(Threshold, thresh) cv2.imshow(Results, result_img) cv2.waitKey(0) else: # matplotlib显示代码总结本文详细介绍了两种基于OpenCV的图像测量方法它们各有优缺点和适用场景。方法一实现简单适合规则排列物体的测量方法二封装性更好结果展示更丰富适合更通用的测量场景。在实际应用中可以根据具体需求选择合适的方法或者结合两种方法的优点进行改进。例如可以使用方法二的封装结构同时允许用户选择不同类型的参考物体从而提高测量的灵活性和准确性。图像测量技术在工业检测、物体识别、机器人视觉等领域有着广泛的应用前景通过不断优化算法和提高测量精度可以进一步拓展其应用范围。