#平台:pycharm
#语言:python
#任务:图像尺寸测量
#器件:平板电脑+USB摄像头
# 最终优化后的版本
from scipy.spatial.distance import euclidean
from imutils import perspective
from imutils import contours
import cv2
import numpy as np
import imutils
#红色轮廓线,黄色最小外接矩形
cam = True
cap = cv2.VideoCapture(1) # 视频捕获 打开摄像头
cap.set(3, 960) # 3序号 视频流的帧的宽度960
cap.set(4, 540) # 4序号 视频流的帧的高度540
cap.set(10, 100) # 10序号 视频流的帧的亮度
image = cv2.imread("G:/pycharmonemy/5.png")
while True:
if cam:
success, img = cap.read()
else:
img = cv2.imread(image)
# 预处理
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 0) # 高斯模糊 高斯核大小5*5 标准差取0 模糊处理便于寻找边缘和特征点
imgCanny = cv2.Canny(imgBlur, 100, 100) # Canny边缘检测 用双阈值确定边缘 阈值越小,能够捕获越多的边缘信息
kernel = np.ones((5, 5), np.uint8) # 定义8位无符号整数5*5大小
imgDilation = cv2.dilate(imgCanny, kernel, iterations=1) # 膨胀 膨胀处理可以将断裂开的目标物进行合并,便于对其整体的提取
imgErode = cv2.erode(imgDilation, kernel, iterations=1) # 腐蚀 腐蚀处理可以将粘连在一起的不同目标物分离,并可以将小的颗粒噪声去除
cnts = cv2.findContours(imgErode.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# 设置最左边的轮廓为参考对象,从左到右对轮廓进行排序
if len(cnts):
(cnts, _) = imutils.contours.sort_contours(cnts)
# 删除小轮廓
if len(cnts):
cnts = [x for x in cnts if cv2.contourArea(x) > 100]
# 参考对象尺寸选择4cm长,2cm宽的长方形
if len(cnts):
ref_object = cnts[0]
# 计算最小的外接矩形
box = cv2.minAreaRect(ref_object)
box = cv2.boxPoints(box)
box = np.array(box, dtype="int")
# 对轮廓点进行排序 左上角,右上角,右下角,左下角
box = perspective.order_points(box)
# 获取4个坐标点并计算中心点坐标
(tl, tr, br, bl) = box
# 根据左上角参考物体,计算单位长度中的像素个数
dist_in_wpixel = euclidean(tl, tr)
dist_in_hpixel = euclidean(tr, br)
dist_in_wcm = 4.0
dist_in_hcm = 2.0
pixel_per_hcm = dist_in_hpixel / dist_in_hcm # 计算纵向比例系数
pixel_per_wcm = dist_in_wpixel / dist_in_wcm # 计算横向比例系数
# 轮廓检测
contours, hierarchy = cv2.findContours(imgErode.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for obj in contours:
area = cv2.contourArea(obj) # 计算轮廓内区域的面积
if area > 500:
cv2.drawContours(img, obj, -1, (0, 0, 255), 3) # 绘制红色轮廓线
peri = cv2.arcLength(obj, True) # 计算轮廓的周长,true表示轮廓为封闭
approx = cv2.approxPolyDP(obj, 0.02 * peri, True) # 获取轮廓角点坐标
CornerNum = len(approx) # 统计轮廓角点的数量
x, y, w, h = cv2.boundingRect(approx) # 获取坐标值和宽度、高度
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 2) # 绘制边界框
# 轮廓对象分类 根据点的个数确定图形形状
if CornerNum == 4:
aspRatio = w / float(h)
if aspRatio > 0.96 and aspRatio < 1.04:
objectshape = "square" # 判断为正方形
else:
objectshape = "rectangle" # 判断为长方形
elif CornerNum > 4:
objectshape = "circle" # 判断为圆形
else:
objectshape = "None" # 判断为不符合要求的形状
cv2.putText(img, objectshape, (x + (w // 2) - 10, y + (h // 2) - 10), cv2.FONT_HERSHEY_COMPLEX, 0.7, (0, 0, 0),
2) # 标注图形类别
# 绘制待测图形最小外接矩形
for cnt in range(len(cnts)):
box = cv2.minAreaRect((cnts)[cnt])
box = cv2.boxPoints(box)
box = np.array(box, dtype="int")
box = perspective.order_points(box)
(tl, tr, br, bl) = box
mid_pt_horizontal = (tl[0] + int(abs(tr[0] - tl[0])/2), tl[1] + int(abs(tr[1] - tl[1])/2))
mid_pt_verticle = (tr[0] + int(abs(tr[0] - br[0])/2), tr[1] + int(abs(tr[1] - br[1])/2))
wid = euclidean(tl, tr)/pixel_per_wcm
ht = euclidean(tr, br)/pixel_per_hcm
cv2.putText(img, "{:.2f}cm".format(wid), (int(mid_pt_horizontal[0] - 15), int(mid_pt_horizontal[1] - 10)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
cv2.putText(img, "{:.2f}cm".format(ht), (int(mid_pt_verticle[0] + 10), int(mid_pt_verticle[1])),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
cv2.imshow("pattern measurement", img)
if cv2.waitKey(1) & 0xFF == ord('k'): # 检测键盘,按下k键时退出摄像头拍摄界面
break
效果展示