目录
前言
注意:不讲实现原理,也没有做UI,精度就玩玩的级别,记得打(尽量柔和的)光。
博主是一名机械设计制造及其自动化专业的学生,以前在车间上课时总需要挑选特定尺寸的毛坯作为被加工工件,奈何本人较懒,所以就有了码这么一个py文件出来助我偷懒的想法。
完整的文件(某U加速”学术资源”可以访问):
- https://github.com/Yjie0929/object-size-measurement-based-on-OpenCV.git
; 一、开发前准备
喜欢用Pycharm还是Anaconda或其它都可以,没有关系。
因为摄像头使用的只是普通的家用摄像头(某夕夕个位数包邮),所以在码程序之前需要准备一个尺寸精度较高(尽量高)的参照物来获取欧氏距离和真实长度的比率。
穷得只能3D打印的屑博主:10mm³,20mm³,30mm³
二、需要的库
from scipy.spatial.distance import euclidean
import numpy as np
import imutils
import time
import cv2
三、程序主体
3.0 mian()
if __name__ == '__main__':
camera_type = set_camera_type()
filter_area, reference_points = reference_processing()
rate = rate_calculation()
real_time_processing()
3.1设置被调用的摄像头类型
这段函数是为了方便程序能够在内置相机或外置相机之间来回切换工作。如果确定仅使用外置相机的情况下可以忽略这一步。
def set_camera_type():
while True:
try:
set_type = int(input('摄像头调用(输入数字代号:0.内置,1.外置):'))
except ValueError:
delay('输入参数类型错误')
continue
else:
if (set_type < 0) or (set_type > 1):
delay('输出参数不在范围内')
continue
elif set_type == 0:
print('选择:内置摄像头')
else:
print('选择:外置摄像头')
break
return set_type
3.2调用相机
如确认仅使用外置相机时将camera_type设置为’1’,cv2.CAP_DSHOW为可选参数,在相机调用过程中出现不知名报错时试着加入。关于第二个if,是防止遇到窗口关闭了但又没有完全关闭的情况而导致的堵塞。
def call_camera():
camera = cv2.VideoCapture(camera_type, cv2.CAP_DSHOW)
if camera.isOpened() is False:
print('摄像头调用失败')
raise AssertionError
else:
while True:
frame = camera.read()[1]
image = cv2.flip(frame, 1, dst=None)
cv2.imshow('Camera', image)
if (cv2.waitKey(1) > -1) or (cv2.getWindowProperty('Camera', cv2.WND_PROP_VISIBLE) < 1.0):
cv2.destroyWindow('Camera')
break
return image
3.3图像处理(轮廓端点查找)
cv2.Canny中的min_val与max_val参数值的大小可以判断是否为边,且 值越小,拾取到的边缘信息就越多。
- cv2.findContours有2个返回值,分别是contours和hierarchy,前者是被检测到的轮廓信息,后者意义不明。
- imutils.grab_contours用来获取 cv2.findContours的contours,contours才是需要被用于计算的数据。
def get_points(image):
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gaussian_blur = cv2.GaussianBlur(gray_image, (5, 5), 0)
min_val, max_val = 50, 100
margin = cv2.Canny(gaussian_blur, min_val, max_val)
open_margin = cv2.dilate(margin, None, iterations=15)
contours = cv2.findContours(open_margin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
points = imutils.grab_contours(contours)
return points
3.4边框绘制(数据计算)
这一部分程序的用处主要是绘制框架与数据计算。程序前期在对参照物对象拍照时需要绘制框架呈现出被选中的对像,在程序后期除了要绘制框架外,还要通过比率计算真实长度、面积,最后在绘制框架的同时把计算结果也显示出来。
def draw_frame(image, points, tag):
if tag == 0:
for point in points:
min_area = cv2.minAreaRect(point)
min_area_point = cv2.boxPoints(min_area)
int_point = [min_area_point.astype('int')]
cv2.drawContours(image, int_point, -1, (0, 0, 255), 1)
return min_area_point
else:
for point in points:
min_area = cv2.minAreaRect(point)
min_area_point = cv2.boxPoints(min_area)
left_point, right_point = min_area_point[0], min_area_point[1]
X = left_point[0] + int(abs(right_point[0] - left_point[0]) / 2)
Y = left_point[1] + int(abs(right_point[1] - left_point[1]) / 2)
int_point = [min_area_point.astype('int')]
cv2.drawContours(image, int_point, -1, (0, 0, 255), 1)
radius = (euclidean(left_point, right_point) / 2) / rate
area = int((3.1415926 * pow(radius, 2)))
cv2.putText(image, '{}'.format(area), (int(X), int(Y)), cv2.FONT_HERSHEY_SIMPLEX, 5, (0, 0, 255), 5)
我的检测对象一般以圆形为主,所以只需要取出最左、最右的两个点坐标就能够用于计算了
min_area_point = cv2.boxPoints(min_area)
left_point, right_point = min_area_point[0], min_area_point[1]
3.5比率计算
比率是参照物两点在度量空间内两点距离和真实距离的比值,本项目后期所有的计算尺寸均由欧氏距离比上比率得出。
def rate_calculation():
delay('计算比率')
left_point, right_point = reference_points[0], reference_points[1]
length_euclidean = euclidean(left_point, right_point)
while True:
try:
length_reference = int(input('输入参照物长度(mm):'))
except ValueError:
delay('输入参数类型错误')
continue
else:
if length_reference 0:
delay('参数不可小于或等于0')
continue
else:
break
rate = length_euclidean / length_reference
print('(参照物)欧氏长度:{}mm'.format(length_euclidean))
print('(参照物)实际长度:{}mm'.format(length_reference))
print('长度比率:{}'.format(rate))
return rate
3.6参照物选取(拍照)
在调试过程中有多次遇到过拍照后参照物 选取不正确,为了防止这一情况的出现就设置了while循环,只有在手动确认参照物被正常框选的情况下才能进入下一步。
值得关注的是selected_points 的筛选方式是采用了将筛选面积不断加一,直到只剩下参照物对象的方式,即len(selected_points) = 1。
def reference_processing():
circulation = True
while circulation:
image = call_camera()
points = get_points(image)
selected_points = []
filter_area = 1
while True:
[selected_points.append(i) for i in points if cv2.contourArea(i) > filter_area]
if len(selected_points) > 1:
selected_points.clear()
filter_area += 1
else:
break
reference_area_point = draw_frame(image, selected_points, 0)
while True:
cv2.imshow('reference', image)
if (cv2.waitKey(1) > -1) or (cv2.getWindowProperty('reference', cv2.WND_PROP_VISIBLE) < 1.0):
cv2.destroyWindow('reference')
break
while circulation:
try:
tag = str(input('是否是理想参照物(Y/N):'))
except ValueError:
delay('输入参数类型错误')
continue
else:
if (tag == 'Y') or (tag == 'y'):
circulation = False
break
elif (tag == 'N') or (tag == 'n'):
break
return filter_area, reference_area_point
3.7实时测量
这段就不多说了,和前面基本一样的原理。
def real_time_processing():
print('进入实时测量,按下回车键结束程序')
camera = cv2.VideoCapture(camera_type, cv2.CAP_DSHOW)
while True:
frame = camera.read()[1]
image = cv2.flip(frame, 1, dst=None)
points = get_points(image)
selected_points = []
[selected_points.append(i) for i in points if cv2.contourArea(i) > filter_area]
draw_frame(image, selected_points, 1)
cv2.imshow('Camera', image)
if (cv2.waitKey(1) > -1) or (cv2.getWindowProperty('Camera', cv2.WND_PROP_VISIBLE) < 1.0):
cv2.destroyWindow('Camera')
break
四、成果展示
参照物拍照:(指方为圆)
实时测量:(指方为圆),这里摄像头高度发生了变化,拍摄角度也出现误差,可以采用只存储最小值数据尽量保证精度。
Original: https://blog.csdn.net/lyj3112424/article/details/122315162
Author: 麦克斯韦除妖
Title: 使用Python-OpenCV实时测量物体的尺寸大小(仅供参考)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/701292/
转载文章受原作者版权保护。转载请注明原作者出处!