QT+Python+OpenCV+视觉尺寸测量
开源项目Time-Object-Measurement-maste:源代码(2K相机)
ObjectMeasurement.py
import cv2 # 导入OpenCV库,用于图像处理
import utlis # 导入自定义的工具库,可能包含一些辅助函数
# 配置参数
webcam = True # 是否使用摄像头,True表示使用,False表示使用静态图片
image_path = 'C:/Users/CJL/Desktop/Real-Time-Object-Measurement-master/1.jpg' # 静态图片的路径
cap = cv2.VideoCapture(0) # 打开默认摄像头(设备ID为0)
cap.set(10, 160) # 设置摄像头的亮度(属性ID 10, 值为160)
cap.set(3, 2560) # 设置摄像头捕获图像的宽度为2560
cap.set(4, 1440) # 设置摄像头捕获图像的高度为1440
scale = 3 # 图像处理时的缩放比例
width_processed = 210 * scale # 处理后的图像宽度
height_processed = 297 * scale # 处理后的图像高度
def process_image(img, scale, width_processed, height_processed):
"""
处理图像,包括获取轮廓、变换图像、测量对象尺寸并绘制结果。
:param img: 输入的原始图像
:param scale: 缩放比例
:param width_processed: 处理后的图像宽度
:param height_processed: 处理后的图像高度
"""
img_contours, contours = utlis.getContours(img, minArea=50000, filter=4) # 获取图像中的轮廓
if contours: # 如果检测到轮廓
biggest = contours[0][2] # 获取最大的轮廓
img_warped = utlis.warpImg(img, biggest, width_processed, height_processed) # 根据最大轮廓变换图像
img_contours2, contours2 = utlis.getContours(img_warped, minArea=2000, filter=4, cThr=[50, 50], draw=False)
# 在变换后的图像中再次获取轮廓,不进行绘制
for obj in contours2:
cv2.polylines(img_contours2, [obj[2]], True, (0, 255, 0), 2) # 绘制轮廓
points = utlis.reorder(obj[2]) # 重新排序轮廓点
width = round((utlis.findDis(points[0][0] // scale, points[1][0] // scale) / 10), 1) # 计算宽度
height = round((utlis.findDis(points[0][0] // scale, points[2][0] // scale) / 10), 1) # 计算高度
# 绘制宽度和高度线
cv2.arrowedLine(img_contours2, tuple(points[0][0]), tuple(points[1][0]), (255, 0, 255), 3, 8, 0, 0.05)
cv2.arrowedLine(img_contours2, tuple(points[0][0]), tuple(points[2][0]), (255, 0, 255), 3, 8, 0, 0.05)
x, y, w, h = obj[3] # 获取轮廓的边界框
# 在图像上绘制宽度和高度文本
cv2.putText(img_contours2, f'{width}cm', (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
(255, 0, 255), 2)
cv2.putText(img_contours2, f'{height}cm', (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
(255, 0, 255), 2)
cv2.imshow('Processed Image', img_contours2) # 显示处理后的图像
def main_loop():
"""
主循环函数,用于持续读取摄像头或图像,并进行处理。
"""
while True:
if webcam: # 如果使用摄像头
success, img = cap.read() # 读取摄像头图像
if not success: # 如果读取失败
print("Failed to read from camera")
break
else: # 如果使用静态图片
img = cv2.imread(image_path) # 读取静态图片
if img is None: # 如果读取失败
print("Failed to read image from path")
break
process_image(img, scale, width_processed, height_processed) # 处理图像
img_resized = cv2.resize(img, (0, 0), None, 0.5, 0.5) # 缩放原始图像以便显示
cv2.imshow('Original Image', img_resized) # 显示原始图像
if cv2.waitKey(1) & 0xFF == ord('q'): # 如果按下'q'键,则退出循环
break
if __name__ == "__main__":
main_loop() # 运行主循环
cap.release() # 释放摄像头资源
cv2.destroyAllWindows() # 销毁所有OpenCV窗口
utlis.py
import cv2 # 导入OpenCV库,用于图像处理
import numpy as np # 导入NumPy库,用于数组和矩阵运算
def getContours(img, cThr=[100, 100], showCanny=False, minArea=1000, filter=0, draw=False):
"""
获取图像中的轮廓
参数:
img: 输入的图像
cThr: Canny边缘检测的阈值,格式为[低阈值, 高阈值],默认为[100, 100]
showCanny: 是否显示Canny边缘检测结果,默认为False
minArea: 轮廓的最小面积,低于该值的轮廓将被过滤掉,默认为1000
filter: 轮廓边数的过滤条件,默认为0(不过滤)
draw: 是否在原图上绘制轮廓,默认为False
返回:
img: 处理后的图像(如果draw为True,则为绘制了轮廓的图像)
finalCountours: 检测到的轮廓信息列表,每个元素包含[边数, 面积, 近似多边形, 边界框, 原始轮廓]
"""
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将图像转换为灰度图
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1) # 使用高斯模糊平滑图像
imgCanny = cv2.Canny(imgBlur, cThr[0], cThr[1]) # 使用Canny算法检测边缘
kernel = np.ones((5, 5)) # 创建5x5的卷积核
imgDial = cv2.dilate(imgCanny, kernel, iterations=3) # 膨胀操作,增强边缘
imgThre = cv2.erode(imgDial, kernel, iterations=2) # 腐蚀操作,去除杂点
if showCanny:
cv2.imshow('Canny', imgThre) # 显示Canny边缘检测结果
contours, hierarchy = cv2.findContours(imgThre, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 查找轮廓
finalCountours = []
for i in contours:
area = cv2.contourArea(i) # 计算轮廓面积
if area > minArea:
peri = cv2.arcLength(i, True) # 计算轮廓周长
approx = cv2.approxPolyDP(i, 0.02 * peri, True) # 使用多边形逼近轮廓
bbox = cv2.boundingRect(approx) # 获取轮廓的边界框
if filter > 0:
if len(approx) == filter: # 根据边数过滤轮廓
finalCountours.append([len(approx), area, approx, bbox, i])
else:
finalCountours.append([len(approx), area, approx, bbox, i])
finalCountours = sorted(finalCountours, key=lambda x: x[1], reverse=True) # 按面积降序排序轮廓
if draw:
for con in finalCountours:
cv2.drawContours(img, con[4], -1, (0, 0, 255), 3) # 在图像上绘制轮廓
return img, finalCountours
def reorder(myPoints):
"""
重新排序四个点的顺序,使其符合:左上,右上,右下,左下
参数:
myPoints: 输入的四个点坐标,通常是一个形状为(4, 2)或(8,)的数组
返回:
myPointsNew: 重新排序后的点坐标
"""
myPointsNew = np.zeros_like(myPoints)
myPoints = myPoints.reshape((4, 2))
add = myPoints.sum(1) # 按x+y的值排序,找到最小和最大的点
myPointsNew[0] = myPoints[np.argmin(add)] # 左上点
myPointsNew[3] = myPoints[np.argmax(add)] # 右下点
diff = np.diff(myPoints, axis=1) # 按x-y的值排序,找到最小和最大的点
myPointsNew[1] = myPoints[np.argmin(diff)] # 右上点
myPointsNew[2] = myPoints[np.argmax(diff)] # 左下点
return myPointsNew
def warpImg(img, points, w, h, pad=20):
"""
根据给定的四个点,对图像进行透视变换
参数:
img: 输入的图像
points: 用于透视变换的四个点坐标
w: 变换后图像的宽度
h: 变换后图像的高度
pad: 变换后图像四周的裁剪边距,默认为20
返回:
imgWarp: 经过透视变换和裁剪后的图像
"""
points = reorder(points) # 重新排序点
pts1 = np.float32(points) # 原始点坐标
pts2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]]) # 目标点坐标
matrix = cv2.getPerspectiveTransform(pts1, pts2) # 计算透视变换矩阵
imgWarp = cv2.warpPerspective(img, matrix, (w, h)) # 进行透视变换
imgWarp = imgWarp[pad:imgWarp.shape[0] - pad, pad:imgWarp.shape[1] - pad] # 裁剪图像
return imgWarp
def findDis(pts1, pts2):
"""
计算两点之间的欧氏距离
参数:
pts1: 第一个点的坐标 (x1, y1)
pts2: 第二个点的坐标 (x2, y2)
返回:
距离值
"""
return ((pts2[0] - pts1[0]) ** 2 + (pts2[1] - pts1[1]) ** 2) ** 0.5
开源项目Time-Object-Measurement-maste:效果展示
开源项目Time-Object-Measurement-maste:修改源代码,接入海康威视摄像机(1080p)
# 配置参数
webcam = True # 设置是否使用网络摄像头(RTSP流)
url = "rtsp://admin:Admin123@192.168.31.64:554/h264/ch33/main/av_stream" # RTSP流的URL
cap = cv2.VideoCapture(url) # 创建VideoCapture对象,用于从RTSP流捕获视频
cap.set(10, 160) # 设置摄像头的亮度(可能不适用于所有摄像头或流)
cap.set(3, 1920) # 设置捕获视频的宽度
cap.set(4, 1080) # 设置捕获视频的高度
scale = 3 # 图像处理的缩放比例
width_processed = 210 * scale # 处理后的图像宽度
height_processed = 297 * scale # 处理后的图像高度
开源项目Time-Object-Measurement-maste:运行效果
模糊