工业视觉踩坑实录(二):工业粉尘检测踩坑3次后,我用“暗通道先验“搞定了
本文分享了工业粉尘检测的实战经验。作者在工厂异物检测项目中,发现传统YOLO模型受粉尘干扰严重失效,转而采用"暗通道先验"去雾算法来解决这一问题。文章详细解析了粉尘与雾霾的相似性,介绍了暗通道原理及其在粉尘检测中的应用,并提供了完整的Python实现代码。针对工业场景特有的光照变化、运动干扰和镜头脏污等问题,给出了实用的解决方案。该方案无需额外传感器,仅通过视觉算法即可实时检测
工业粉尘检测踩坑3次后,我用"暗通道先验"搞定了

关于作者
我接触视觉整整 10 年。
机器视觉、烟草、煤矿等行业都有深度开发经验。从硬件选型、算法开发、模型训练,到上位机开发及部署,都在一线磨过。
之前是多家公司人工智能团队的技术负责人。现在自己创业了,还在继续做视觉落地这件事。
作者说
在做视觉这件事之前,我以为最难的是算法开发和模型精度。
后来发现,真正让人崩溃的从来不是算法本身,而是现场那些"没想到"的情况——
- 摄像头突然逆光,画面一片白
- 皮带头晃动,带动的尘雾让检测全乱
- 机器停了,但传感器报告还在"运行"
- 边缘设备算力不够,一运行就卡死
这些问题是实验室里遇不到的。
所以我打算写一个系列,记录我们在真实工业场景下踩过的坑、解决过的问题。不讲多么酷炫的算法,只聊怎么让算法稳定跑在客户现场。
这是第二篇。
踩坑实录:粉尘上来后,我的算法全崩了
那是一个工厂项目。
客户说需求很简单:检测传送带上有没有异物。我信心满满地用 YOLO 训了一个模型,现场部署,测试——
第一天,完美。
第二天早上,现场电话打过来:模型彻底失灵了。
我一看监控画面,好家伙——粉尘全起来了,整个屏幕白茫茫一片,YOLO 框得满屏都是"异物",其实全是误检。
后来才知道,工厂车间下午生产高峰期粉尘浓度会爆表,根本无法正常检测。
这是我踩的第一个坑:没考虑粉尘干扰。
01 问题的本质:粉尘与雾霾的相似性
1.1 粉尘即"人造雾"
从成像角度看,粉尘和雾霾本质上是同一类问题:
- 都是空气中悬浮的颗粒物
- 都会导致光线散射
- 都会让图像出现以下特征:
- 对比度下降:画面变"灰"
- 细节丢失:远处物体模糊
- 颜色偏移:色彩变淡、发白
所以,雾霾去雾的算法,可以用来检测粉尘。
1.2 传统方法的局限
在工业场景下,传统的粉尘检测方法有:
| 方法 | 原理 | 局限 |
|---|---|---|
| 激光散射 | 测量粒子散射光强 | 需要额外传感器 |
| 重量法 | 称重过滤后的粉尘 | 离线、无法实时 |
| 摄像头 + 阈值 | 简单判断画面亮度 | 误检率高,受光照影响大 |
我们需要一种纯视觉、实时、鲁棒的方案。
02 暗通道先验原理
2.1 什么是暗通道?
暗通道先验(Dark Channel Prior)是 2009 年何恺明等人提出的,论文标题是《Single Image Haze Removal Using Dark Channel Prior》。
核心发现:在无雾图像中,几乎每个局部区域都存在至少一个"暗通道"像素——即某个通道的强度值非常低。
简单理解:自然场景中,总有一些很暗的地方(比如树叶阴影、黑色汽车、深色建筑)。
2.2 粉尘图像的暗通道
当有粉尘时,这些暗通道的强度会显著提升——因为粉尘颗粒会让光线散射,覆盖了原本的暗细节。
因此:
暗通道强度 ≈ 粉尘程度
2.3 透射率估算
基于暗通道,可以估算出图像的透射率(Transmission)——即每个像素有多少光线是通过粉尘层到达摄像头的。
2.4 粉尘检测公式
我们的核心公式:
粉尘浓度 = f(暗通道强度, 透射率, 光照估计)
实际使用时,我们做了简化:
def estimate_dust_level(img):
# 计算暗通道
dark_channel = np.min(img, axis=2)
# 形态学闭操作,去噪
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15))
dark_channel = cv2.morphologyEx(dark_channel, cv2.MORPH_CLOSE, kernel)
# 计算平均强度
dust_score = np.mean(dark_channel)
return dust_score
03 落地避坑指南
坑1:光照干扰
问题:开灯/关灯时,暗通道计算会误判。
解决:增加光照判断,先区分"开灯/关灯"状态,再用不同阈值。
坑2:运动物体干扰
问题:皮带上物体快速移动时,会被误判为粉尘。
解决:结合光流法,只在"静态区域"计算粉尘。
坑3:镜头脏污
问题:镜头沾灰,算法以为是粉尘。
解决:定期自检 + 差分法剔除。
04 完整代码
import cv2
import numpy as np
def preprocess_image(img):
"""图像预处理"""
# 自动白平衡
result = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
avg_a = np.average(result[:, :, 1])
avg_b = np.average(result[:, :, 2])
result[:, :, 1] = result[:, :, 1] - ((avg_a - 128) * (result[:, :, 0] / 255.0) * 1.1)
result[:, :, 2] = result[:, :, 2] - ((avg_b - 128) * (result[:, :, 0] / 255.0) * 1.1)
img = cv2.cvtColor(result, cv2.COLOR_LAB2BGR)
return img
def dark_channel(img, window_size=15):
"""计算暗通道"""
min_img = np.min(img, axis=2)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (window_size, window_size))
dark = cv2.erode(min_img, kernel)
return dark
def estimate_atmospheric_light(img, dark):
"""估计全局光照"""
h, w = dark.shape
num_pixels = h * w
num_top = int(max(num_pixels // 1000, 1))
flat_dark = dark.flatten()
flat_img = img.reshape(num_pixels, 3)
idx = np.argsort(flat_dark)[-num_top:]
atmospheric_light = np.max(flat_img[idx], axis=0)
return atmospheric_light
def estimate_transmission(img, atmospheric_light, window_size=15):
"""估计透射率"""
img = np.float64(img) / atmospheric_light
transmission = 1 - dark_channel(img, window_size)
transmission = np.clip(transmission, 0.1, 1.0)
return transmission
def dust_detection(img, threshold=0.4):
"""主函数:检测粉尘浓度"""
img = preprocess_image(img)
dark = dark_channel(img)
atmospheric_light = estimate_atmospheric_light(img, dark)
transmission = estimate_transmission(img, atmospheric_light)
dust_level = 1 - np.mean(transmission)
is_dusty = dust_level > threshold
return {
'dust_level': dust_level,
'is_dusty': is_dusty,
'transmission': transmission,
'atmospheric_light': atmospheric_light
}
# 使用示例
# cap = cv2.VideoCapture(0)
# while True:
# ret, frame = cap.read()
# if not ret:
# break
# result = dust_detection(frame)
# print(f"Dust Level: {result['dust_level']:.2f}")
# cv2.imshow('frame', frame)
# if cv2.waitKey(1) & 0xFF == ord('q'):
# break
05 总结
这篇文章分享了我们在工业粉尘检测中的实战方案:
- 核心思路:借用"暗通道先验"去雾算法,转用于粉尘检测
- 关键优势:纯视觉、无需额外传感器、实时可用
- 落地经验:光照自适应 + 运动过滤 + 镜头自检
如果觉得有帮助,点个赞支持一下,后面我会继续更新边缘设备部署、数据闭环实战等系列文章。
有问题评论区见!
📎 相关阅读
更多推荐
所有评论(0)