文章目录
一、简介:
液位检测在工业生产中具有重要意义,它关乎生产安全、效率和质量。传统的液位检测方法如浮子式、压力式等存在一定的局限性。随着图像处理技术的发展,利用摄像头结合传统图像处理方法进行液位识别逐渐成为一种趋势。本文将以一个简单的温度计温度识别为例子,带你了解如何使用传统图像处理技术实现液位识别。
二、温度计液位检测所用的方案:
本方案首先采用伽马变换对液位颜色进行增强处理,以提升液位与背景的对比度;接着利用Otsu二值化方法对增强后的图像进行分割,明确区分液位与背景;然后通过形态学变换去除图像中的文字和刻度,以获得干净的液位图;最终,根据像素映射关系,将液位高度准确映射回实际距离,从而实现液位的精确测量。
三、算法实现步骤
3.1液位高度标定
标定液位高度即确定温度计量程所代表的像素高度,标定方法为:计算温度计上下端量程所对应的y值,二者相减获得量程的高度差也就是下图蓝线所代表的像素值:
获取y值代码:
import cv2
##此代码可以返回鼠标点击的点位置,点在温度计量程上
# 鼠标回调函数
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN: # 检测到左键单击
# 获取并打印鼠标点击位置的像素值
pixel_value = image[y, x]
print(f"位置 ({x}, {y}) 的像素值为: {pixel_value}")
# 读取图像
image = cv2.imread(r'F:\2024-07-20\11.jpg')
# 检查图像是否成功加载
if image is None:
print("Error: 图像无法加载。请检查路径是否正确。")
else:
# 创建窗口
cv2.namedWindow('Image')
# 设置鼠标回调函数
cv2.setMouseCallback('Image', mouse_callback)
# 显示图像
while True:
cv2.imshow('Image', image)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 关闭所有窗口
cv2.destroyAllWindows()
3.2 伽马增强增强液柱信息
伽马变换是一种图像处理技术,它通过调整图像的亮度和对比度来改善视觉效果。简单来说,它改变了图像中每个像素的亮度级别:如果伽马值大于1,图像的暗部会变得更亮,而亮部则相对保持不变;如果伽马值小于1,则亮部会变得更亮,暗部则相对变暗。这种变换有助于使图像看起来更加自然,尤其是在光线不足或过亮的情况下。
实现代码:
def gamma_trans(img, gamma): # 定义一个gamma校正函数
# 创建一个查找表,用于将原始像素值映射到gamma校正后的像素值
gamma_table = [np.power(x / 255.0, gamma) * 255.0 for x in range(256)]
# 将查找表转换为numpy数组,并四舍五入到最近的整数
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
# 使用OpenCV的LUT函数进行颜色映射
return cv2.LUT(img, gamma_table)
效果如下:
左侧为增强前,右侧为增强后,增强后液柱的特征更明显
3.3 大津算法与形态学变换提取液位信息
大津算法(Otsu’s Method)是一种自动确定图像二值化阈值的方法。它的基本思想是寻找一个阈值,使得图像中前景和背景的类间方差最大,同时类内方差最小。简而言之,就是通过计算不同阈值下的类间方差,找到最优阈值,将图像分为前景和背景两部分,以达到最佳分割效果。大津算法适用于图像前景和背景对比度明显的情况,是一种广泛使用的图像分割技术.
形态学变换是图像处理中的一种基本操作,它基于数学形态学的理论,通过结构元素对图像进行一系列的形态操作。这些操作通常包括膨胀、腐蚀、开运算和闭运算等,用于提取图像中的有用信息,如边界、轮廓、连通区域等,同时可以去除噪声、平滑边缘、填充孔洞等。
根据大津二值化与形态学闭运算处理无用信息,获取液位信息,提取后的信息效果如下:
实现代码:
# 定义结构元素,用于形态学操作
kernel = np.ones((11, 11), np.uint8)
# 应用Otsu二值化
_, th = cv2.threshold(gam, 0, 255, cv2.THRESH_OTSU)
# 使用闭运算去除刻度线以及文字
close = cv2.morphologyEx(th, cv2.MORPH_CLOSE, kernel)
3.4 筛选图像轮廓选择液位处轮廓
代码如下(示例):
contours, _ = cv2.findContours(close_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 按轮廓面积降序排序
contours = sorted(contours, key=cv2.contourArea, reverse=True)
max_contour = contours[0] # 获取最大的轮廓
3.5 计算液位像素点高度并映射回实际值
points = max_contour[:, 0, :] # 提取轮廓中的所有点
# 按照y坐标对点进行排序
sorted_points = points[np.argsort(points[:, 1])]
# 计算液位高度,并转换为实际温度
temp = value_pix2 - sorted_points[0][1] # 液柱最上面的点的y值距离底边的像素值长度
temperature = temp * (value_true / value_pix) - 30 # 转换为温度值 30为温度计的下限零下三十度
print("温度为", temperature)
四、代码实现
import cv2
import numpy as np
# 定义一些常量,用于后续计算
value_pix2 = 475 # 图像底部固定的像素位置
value_pix = 394 # 图像底部和顶部之间的像素差,用于计算液位高度
value_true = 80 # 此款温度计总共有八十个刻度
def gamma_trans(img, gamma): # 定义一个gamma校正函数
# 创建一个查找表,用于将原始像素值映射到gamma校正后的像素值
gamma_table = [np.power(x / 255.0, gamma) * 255.0 for x in range(256)]
# 将查找表转换为numpy数组,并四舍五入到最近的整数
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
# 使用OpenCV的LUT函数进行颜色映射
return cv2.LUT(img, gamma_table)
# 读取图像,使用灰度模式
img = cv2.imread(r'F:\2024-07-20\11.jpg', 0)
# 定义结构元素,用于形态学操作
kernel = np.ones((11, 11), np.uint8)
# 应用gamma校正和模糊处理
gam = gamma_trans(img, 2.5)
gam = cv2.GaussianBlur(gam, (5, 5), 0)
# 应用Otsu二值化
_, th = cv2.threshold(gam, 0, 255, cv2.THRESH_OTSU)
# 使用闭运算去除刻度线以及文字
close = cv2.morphologyEx(th, cv2.MORPH_CLOSE, kernel)
# 反转图像,以便于后续轮廓检测
close_inv = 255 - close
# 查找并绘制轮廓
contours, _ = cv2.findContours(close_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 按轮廓面积降序排序
contours = sorted(contours, key=cv2.contourArea, reverse=True)
if contours:
max_contour = contours[0] # 获取最大的轮廓
points = max_contour[:, 0, :] # 提取轮廓中的所有点
# 按照y坐标对点进行排序
sorted_points = points[np.argsort(points[:, 1])]
# 计算液位高度,并转换为实际温度
temp = value_pix2 - sorted_points[0][1] # 液柱最上面的点的y值距离底边的像素值长度
temperature = temp * (value_true / value_pix) - 30 # 转换为温度值
print("温度为", temperature)
# 显示处理后的图像
cv2.imshow("ori", img)
cv2.imshow("gam", gam)
cv2.imshow("close", close)
# 等待按键后退出
cv2.waitKey(0)
五:效果:
由于像素误差,温度大概差了0.8度,可以通过图像超分或者对上液位轮廓点取均值进一步降低误差