如何在Python OpenCV光流中设置阈值检测电机线圈振动
基于光流的电机线圈振动状态区分方案
核心思路
光流的**幅值(mag)**直接对应运动强度,是区分振动状态的核心指标——光流可视化的颜色变化本质是幅值/角度的变化,因此优先从幅值入手设计阈值逻辑是最高效的方案。
具体实现方案
1. 基于幅值统计量的固定阈值划分
直接计算裁剪区域内光流幅值的统计特征,通过设定固定阈值区分三种状态:
- 统计整区域的平均幅值:平稳运转时平均幅值极低,低振动时小幅上升,高振动时显著升高
- 统计幅值超过基准值的像素占比:振动时会有大量像素的幅值超过平稳状态的最大值
修改原代码,添加状态判断逻辑:
import cv2 import numpy as np cap = cv2.VideoCapture("Coil.mp4") ret, frame1 = cap.read() frame1 = frame1[284: 383, 498:516] prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) hsv = np.zeros_like(frame1) hsv[..., 1] = 255 # 阈值需根据你的平稳状态视频采集数据调整,以下为示例值 LOW_MAG_THRESH = 10 # 平均幅值低于此值为无振动 HIGH_MAG_THRESH = 30 # 平均幅值高于此值为高振动 HIGH_PIXEL_RATIO_THRESH = 0.2 # 幅值>5的像素占比超20%判定为振动 while cap.isOpened(): ret, frame2 = cap.read() if not ret: break frame2 = frame2[284: 383, 498:516] next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(prvs, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1]) hsv[..., 0] = ang * 180 / np.pi / 2 hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) # 计算幅值统计特征 avg_mag = np.mean(mag) high_mag_pixel_ratio = np.sum(mag > 5) / (mag.shape[0] * mag.shape[1]) # 状态判定 if avg_mag < LOW_MAG_THRESH and high_mag_pixel_ratio < HIGH_PIXEL_RATIO_THRESH: status = "无振动" elif avg_mag < HIGH_MAG_THRESH: status = "低振动" else: status = "高振动" # 在画面显示状态 cv2.putText(rgb, f"状态: {status}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) cv2.imshow('Optical Flow', rgb) k = cv2.waitKey(30) & 0xff if k == 27: break prvs = next_frame cap.release() cv2.destroyAllWindows()
2. 基于光流可视化图的颜色特征阈值
如果你想基于可视化的RGB/HSV颜色判断,可以提取**HSV通道中的V通道(对应幅值归一化值)**的统计值:
- 平稳状态下V通道整体偏暗,像素值集中在0-50区间
- 振动时V通道会出现大量高亮度像素
替换状态判定逻辑即可:
# 从hsv的V通道提取特征 v_channel = hsv[..., 2] avg_v = np.mean(v_channel) bright_pixel_ratio = np.sum(v_channel > 100) / (v_channel.shape[0] * v_channel.shape[1]) # 用V通道统计值判定状态 if avg_v < 30 and bright_pixel_ratio < 0.1: status = "无振动" elif avg_v < 100: status = "低振动" else: status = "高振动"
3. 动态自适应阈值(应对环境波动)
如果电机运转时存在轻微背景波动或环境光变化,可采用滑动窗口平均动态调整阈值:
- 维护最近N帧的平均幅值队列,当当前帧幅值超过队列均值的X倍时判定为振动
- 避免固定阈值带来的误判
示例代码片段:
from collections import deque # 初始化滑动窗口,保存最近20帧的平均幅值 window = deque(maxlen=20) # 先采集10帧平稳状态数据填充窗口 for _ in range(10): ret, frame2 = cap.read() if not ret: break frame2 = frame2[284: 383, 498:516] next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(prvs, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag, _ = cv2.cartToPolar(flow[..., 0], flow[..., 1]) window.append(np.mean(mag)) prvs = next_frame # 后续循环中的动态判定逻辑 avg_mag = np.mean(mag) window.append(avg_mag) window_mean = np.mean(window) window_std = np.std(window) if avg_mag < window_mean + window_std: status = "无振动" elif avg_mag < window_mean + 3*window_std: status = "低振动" else: status = "高振动"
调优建议
- 先采集一段平稳运转的视频,统计幅值的均值、最大值,以此作为阈值的基准
- 调整
cv2.calcOpticalFlowFarneback的参数(如pyr_scale、winsize),可让光流更敏感地捕捉微小振动 - 高振动状态可结合幅值的标准差判断:振动越剧烈,幅值的离散程度越高
内容的提问来源于stack exchange,提问作者S Andrew
相关产品推荐
相关产品推荐

