import cv2
import numpy as np
import time
import collections
# --- 模拟参数 (部分保留用于绘图和逻辑) ---
# WIDTH, HEIGHT 将从视频帧中获取
# CENTER_X, CENTER_Y, ROI_INNER_RADIUS, ROI_OUTER_RADIUS 需要根据实际视频画面调整
SIMULATED_LAMBDA = 0.632 # 模拟激光波长 (例如 HeNe 激光的 0.632 um)
MIRROR_MOVEMENT_PER_FRINGE = SIMULATED_LAMBDA / 2.0 # 每经过一个条纹,反射镜移动 lambda/2
# --- 光流法参数 (保持不变) ---
fb_params = dict(
pyr_scale=0.5, levels=3, winsize=15, iterations=3, poly_n=5, poly_sigma=1.2, flags=0
)
# --- 绘图参数 (保持不变) ---
MAX_GRAPH_POINTS = 200
GRAPH_HEIGHT = 150
GRAPH_WIDTH = 0 # 暂时设为0,将在获取第一帧后更新
time_points = collections.deque(maxlen=MAX_GRAPH_POINTS)
displacement_points = collections.deque(maxlen=MAX_GRAPH_POINTS)
velocity_points = collections.deque(maxlen=MAX_GRAPH_POINTS)
# --- UI 显示参数 (调整高度以适应新的布局) ---
INFO_DISPLAY_HEIGHT = 120 # 增加信息显示区域高度
TOTAL_CANVAS_HEIGHT = 0 # 暂时设为0,将在获取第一帧后更新
display_canvas = None # 暂时设为None
# --- 圆环检测参数 (新增) ---
# 这些参数对霍夫圆变换非常重要,需要根据实际视频调整!
HOUGH_DP = 1
HOUGH_MIN_DIST = 20
HOUGH_PARAM1 = 100
HOUGH_PARAM2 = 30
HOUGH_MIN_RADIUS = 10
HOUGH_MAX_RADIUS = 150
# --- 函数定义 ---
# detect_and_estimate_center 函数将不再在主循环中被调用,但保留其定义
# 如果未来需要切换回动态检测,可以取消注释相关代码行
def detect_and_estimate_center(image_gray, hough_params):
"""
使用霍夫圆变换检测圆环,并估计同心圆的中心。
返回 (cx, cy) 或 None。
"""
# 对图像进行高斯模糊以减少噪声,这有助于霍夫变换
blurred_image = cv2.GaussianBlur(image_gray, (9, 9), 2)
circles = cv2.HoughCircles(blurred_image, cv2.HOUGH_GRADIENT,
hough_params['dp'], hough_params['min_dist'],
param1=hough_params['param1'], param2=hough_params['param2'],
minRadius=hough_params['min_radius'], maxRadius=hough_params['max_radius'])
if circles is not None:
circles = np.uint16(np.around(circles))
# 提取所有检测到的圆心
center_x_coords = circles[0, :, 0]
center_y_coords = circles[0, :, 1]
# 简单地取所有圆心的平均值作为估计的干涉中心
estimated_cx = int(np.mean(center_x_coords))
estimated_cy = int(np.mean(center_y_coords))
return (estimated_cx, estimated_cy)
return None
def analyze_flow_direction(flow, center_x, center_y, inner_r, outer_r):
"""分析光流的径向运动方向 (与原代码相同)"""
h, w = flow.shape[:2]
radial_flow_sum = 0
flow_count = 0
mask = np.zeros((h, w), dtype=np.uint8) # 创建掩码选择ROI
cv2.circle(mask, (center_x, center_y), outer_r, 255, -1)
cv2.circle(mask, (center_x, center_y), inner_r, 0, -1)
# 在ROI内采样分析光流 (优化性能,不必逐像素)
for r_idx in range(inner_r, outer_r, 5): # 每隔5个像素半径采样
for angle_deg in range(0, 360, 15): # 每隔15度采样
angle_rad = np.deg2rad(angle_deg)
x = int(center_x + r_idx * np.cos(angle_rad))
y = int(center_y + r_idx * np.sin(angle_rad))
if 0 <= x < w and 0 <= y < h and mask[y, x] > 0: # 确保在图像和ROI内
fx, fy = flow[y, x]
if abs(fx) < 0.1 and abs(fy) < 0.1: # 忽略非常小的光流
continue
rad_vx = x - center_x
rad_vy = y - center_y
dot_product = fx * rad_vx + fy * rad_vy
radial_flow_sum += np.sign(dot_product) # 只关心方向的累计效应
flow_count += 1
if flow_count > 10: # 需要足够多的有效光流点
return radial_flow_sum / flow_count
return 0
def draw_graph(canvas, x_data_deque, y_data_deque, color, title, y_min, y_max,
graph_x_origin, graph_y_origin, graph_w, graph_h):
"""在指定的canvas区域绘制线图 (与原代码相同)"""
# 绘制图表背景和标题
cv2.rectangle(canvas, (graph_x_origin, graph_y_origin), (graph_x_origin + graph_w, graph_y_origin + graph_h), (40, 40, 40), -1)
cv2.putText(canvas, title, (graph_x_origin + 10, graph_y_origin + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (220, 220, 220), 1)
if not y_data_deque or len(y_data_deque) < 2:
return
# Y轴范围和零位线 (如果y_min和y_max跨越0)
y_range = y_max - y_min
if abs(y_range) < 1e-6: y_range = 1.0 # 避免除以零
if y_min < 0 < y_max:
zero_line_y = graph_y_origin + graph_h - int((-y_min / y_range) * graph_h)
cv2.line(canvas, (graph_x_origin, zero_line_y), (graph_x_origin + graph_w - 10, zero_line_y), (100, 100, 100), 1)
# 准备数据点进行绘制
points = []
num_plot_points = len(y_data_deque)
for i in range(num_plot_points):
# X 坐标:基于数据点在deque中的相对位置,映射到图表宽度
px = graph_x_origin + int((i / (MAX_GRAPH_POINTS - 1 if MAX_GRAPH_POINTS > 1 else 1)) * (graph_w - 10)) # 留出右边距
# Y 坐标:将y值按y_min, y_max缩放到图表高度
py_scaled_val = (y_data_deque[i] - y_min) / y_range
py = graph_y_origin + graph_h - int(py_scaled_val * graph_h) # OpenCV的Y轴是向下的
py = np.clip(py, graph_y_origin, graph_y_origin + graph_h) # 确保在图表区域内
points.append((px, py))
if len(points) >= 2:
cv2.polylines(canvas, [np.array(points, dtype=np.int32)], False, color, 1)
# 绘制Y轴刻度标签
cv2.putText(canvas, f"{y_max:.2f}", (graph_x_origin + graph_w - 45, graph_y_origin + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (180, 180, 180), 1)
cv2.putText(canvas, f"{y_min:.2f}", (graph_x_origin + graph_w - 45, graph_y_origin + graph_h - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (180, 180, 180), 1)
# --- 主循环 ---
# *** 将 "your_video_file.mp4" 替换为你的视频文件实际路径 ***
video_path = "E:\\python files\\参数试调整.mp4"
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"错误:无法打开视频文件 '{video_path}'。请检查文件路径和文件是否存在。")
exit()
# 获取视频分辨率,并设置显示画布大小
ret, frame = cap.read()
if not ret:
print(f"错误:无法从视频文件 '{video_path}' 获取第一帧。")
exit()
if len(frame.shape) == 3:
VIDEO_HEIGHT, VIDEO_WIDTH, _ = frame.shape
else:
VIDEO_HEIGHT, VIDEO_WIDTH = frame.shape
# --- MODIFICATION START ---
# Define fixed center coordinates. THESE ARE NOW CONSTANT.
# Set these values according to where the center of your fringes is in the video.
# If you want to use the (400,387) from your original code:
FIXED_CENTER_X, FIXED_CENTER_Y = (400, 387)
# CURRENT_CENTER_X and CURRENT_CENTER_Y will now simply reference these fixed values
CURRENT_CENTER_X = FIXED_CENTER_X
CURRENT_CENTER_Y = FIXED_CENTER_Y
# --- MODIFICATION END ---
# Calculate widths for left and right panes
LEFT_PANE_WIDTH = VIDEO_WIDTH # Left video area width equals original video width
RIGHT_PANE_WIDTH = VIDEO_WIDTH # Right plotting area width also equals video width, adjust ratio if needed
TOTAL_CANVAS_WIDTH = LEFT_PANE_WIDTH + RIGHT_PANE_WIDTH
# Calculate total canvas height
# Main content area height is the maximum of video height and the total height of two graphs
MAIN_CONTENT_HEIGHT = max(VIDEO_HEIGHT, GRAPH_HEIGHT * 2)
TOTAL_CANVAS_HEIGHT = INFO_DISPLAY_HEIGHT + MAIN_CONTENT_HEIGHT
# Set the actual width for graphs
GRAPH_WIDTH = RIGHT_PANE_WIDTH
# Initialize the main display canvas
display_canvas = np.zeros((TOTAL_CANVAS_HEIGHT, TOTAL_CANVAS_WIDTH, 3), dtype=np.uint8) # BGR
# 请根据你的实际干涉图像调整 ROI
# ROI 半径现在是相对于固定中心
ROI_INNER_RADIUS = int(min(VIDEO_WIDTH, VIDEO_HEIGHT) * 0.10) # 内部半径,例如图像较小维度的10%
ROI_OUTER_RADIUS = int(min(VIDEO_WIDTH, VIDEO_HEIGHT) * 0.27) # 外部半径,例如图像较小维度的40%
prev_gray = None
# 用于环数统计
accumulated_radial_displacement = 0.0 # 累计径向位移 (像素)
# 径向流与环数转换因子 (需要根据实际情况标定)
# 假设一个环的半径变化(涌现/消失)对应50像素的径向位移。
PIXELS_PER_FRINGE = 120 # 这是一个需要根据实际干涉图像和系统进行校准的关键参数。
rings_emerged_count = 0
rings_disappeared_count = 0
# 光流检测状态
current_motion_state_flow = "UNKNOWN"
stable_direction_counter = 0
DIRECTION_CHANGE_THRESHOLD_FLOW = 0.05
STABILITY_FRAMES_FLOW = 5
frame_num = 0
start_time = time.time()
# 图表Y轴动态范围的初始值
disp_min_val, disp_max_val = 0.0, 0.0
vel_min_val, vel_max_val = 0.0, 0.0
# 霍夫圆参数字典,方便传递给函数
hough_params = {
'dp': HOUGH_DP,
'min_dist': HOUGH_MIN_DIST,
'param1': HOUGH_PARAM1,
'param2': HOUGH_PARAM2,
'min_radius': HOUGH_MIN_RADIUS,
'max_radius': HOUGH_MAX_RADIUS
}
print(f"开始分析视频文件: '{video_path}'。按 'q' 退出。")
while True:
frame_num += 1
time_points.append(frame_num)
ret, frame = cap.read()
if not ret:
print("视频播放完毕或无法读取帧。")
break # 视频播放完毕
current_gray_real = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# --- MODIFICATION START ---
# Removed dynamic center detection:
# detected_center = detect_and_estimate_center(current_gray_real, hough_params)
# if detected_center is not None:
# CURRENT_CENTER_X, CURRENT_CENTER_Y = detected_center
# CURRENT_CENTER_X and CURRENT_CENTER_Y remain constant as defined before the loop.
# --- MODIFICATION END ---
# 1. 光流计算与方向分析
avg_radial_flow = 0.0
if prev_gray is not None:
blurred_prev = cv2.GaussianBlur(prev_gray, (5, 5), 0)
blurred_curr = cv2.GaussianBlur(current_gray_real, (5, 5), 0)
flow = cv2.calcOpticalFlowFarneback(blurred_prev, blurred_curr, None, **fb_params)
# analyze_flow_direction 仍然使用 CURRENT_CENTER_X 和 CURRENT_CENTER_Y (现在是固定值)
avg_radial_flow = analyze_flow_direction(flow, CURRENT_CENTER_X, CURRENT_CENTER_Y, ROI_INNER_RADIUS, ROI_OUTER_RADIUS)
# 更新基于光流的运动状态
new_motion_state_candidate_flow = current_motion_state_flow
if avg_radial_flow > DIRECTION_CHANGE_THRESHOLD_FLOW:
detected_direction_str_flow = "OUTWARD (消失)"
elif avg_radial_flow < -DIRECTION_CHANGE_THRESHOLD_FLOW:
detected_direction_str_flow = "INWARD (涌现)"
else:
detected_direction_str_flow = "STATIONARY/UNCERTAIN"
if detected_direction_str_flow == current_motion_state_flow:
stable_direction_counter += 1
elif detected_direction_str_flow != "STATIONARY/UNCERTAIN":
if detected_direction_str_flow == new_motion_state_candidate_flow:
stable_direction_counter += 1
else:
new_motion_state_candidate_flow = detected_direction_str_flow
stable_direction_counter = 1
if stable_direction_counter >= STABILITY_FRAMES_FLOW:
current_motion_state_flow = new_motion_state_candidate_flow
else:
stable_direction_counter = 0
# 2. 环数统计 (基于径向光流累积)
radial_flow_scaled = avg_radial_flow * 10 # 调整这个乘数,使其更接近实际像素位移
accumulated_radial_displacement += radial_flow_scaled
if accumulated_radial_displacement >= PIXELS_PER_FRINGE:
rings_emerged_count += int(accumulated_radial_displacement / PIXELS_PER_FRINGE)
accumulated_radial_displacement %= PIXELS_PER_FRINGE # 保留余数
elif accumulated_radial_displacement <= -PIXELS_PER_FRINGE:
rings_disappeared_count += int(abs(accumulated_radial_displacement) / PIXELS_PER_FRINGE)
accumulated_radial_displacement = - (abs(accumulated_radial_displacement) % PIXELS_PER_FRINGE) # 保留余数,保持负号
prev_gray = current_gray_real.copy()
# 3. 计算位移和速度,用于绘图
current_mirror_velocity = avg_radial_flow
velocity_points.append(current_mirror_velocity)
if len(displacement_points) == 0:
current_mirror_displacement = 0.0
else:
current_mirror_displacement = displacement_points[-1] + current_mirror_velocity
displacement_points.append(current_mirror_displacement)
# 简单动态调整Y轴范围
if displacement_points:
disp_min_val = min(min(displacement_points), disp_min_val, current_mirror_displacement - 0.1)
disp_max_val = max(max(displacement_points), disp_max_val, current_mirror_displacement + 0.1)
if velocity_points:
vel_min_val = min(min(velocity_points), vel_min_val, current_mirror_velocity - 0.01)
vel_max_val = max(max(velocity_points), vel_max_val, current_mirror_velocity + 0.01)
# --- 4. 显示 ---
display_canvas[:, :] = (20, 20, 20) # Fill canvas with dark gray
# A. Information Display Area (Top)
info_area_y_start = 0
# Y-coordinates for text are relative to the top of the info area
cv2.putText(display_canvas, f"Flow Metric (avg_radial_flow): {avg_radial_flow:.3f}", (10, info_area_y_start + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
cv2.putText(display_canvas, f"Emerged Rings: {rings_emerged_count}", (10, info_area_y_start + 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (50, 200, 255), 1)
cv2.putText(display_canvas, f"Disappeared Rings: {rings_disappeared_count}", (10, info_area_y_start + 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (50, 200, 255), 1)
cv2.putText(display_canvas, f"Accumulated Radial Disp: {accumulated_radial_displacement:.2f} px", (10, info_area_y_start + 80), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 100, 100), 1)
# Now shows FIXED center, not "detected"
cv2.putText(display_canvas, f"Fixed Center: ({CURRENT_CENTER_X}, {CURRENT_CENTER_Y})", (10, info_area_y_start + 100), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (100, 255, 255), 1)
# Add status and FPS to the info display
cv2.putText(display_canvas, f"Flow State: {current_motion_state_flow}", (TOTAL_CANVAS_WIDTH // 2 + 10, info_area_y_start + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 255, 50), 1)
cv2.putText(display_canvas, f"FPS: {frame_num / (time.time() - start_time):.2f}", (TOTAL_CANVAS_WIDTH // 2 + 10, info_area_y_start + 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 255, 50), 1)
# B. Video Frame Display Area (Left)
video_display_x_start = 0
video_display_y_start = INFO_DISPLAY_HEIGHT
# Ensure video frame dimensions match the preset VIDEO_WIDTH/HEIGHT
display_frame = cv2.cvtColor(current_gray_real, cv2.COLOR_GRAY2BGR)
if display_frame.shape[0] != VIDEO_HEIGHT or display_frame.shape[1] != VIDEO_WIDTH:
display_frame = cv2.resize(display_frame, (VIDEO_WIDTH, VIDEO_HEIGHT))
display_canvas[video_display_y_start : video_display_y_start + VIDEO_HEIGHT,
video_display_x_start : video_display_x_start + VIDEO_WIDTH] = display_frame
# Draw ROI circles on the live frame using the FIXED center
cv2.circle(display_canvas, (CURRENT_CENTER_X + video_display_x_start, CURRENT_CENTER_Y + video_display_y_start), ROI_OUTER_RADIUS, (0, 0, 255), 2)
cv2.circle(display_canvas, (CURRENT_CENTER_X + video_display_x_start, CURRENT_CENTER_Y + video_display_y_start), ROI_INNER_RADIUS, (0, 0, 255), 2)
cv2.circle(display_canvas, (CURRENT_CENTER_X + video_display_x_start, CURRENT_CENTER_Y + video_display_y_start), 5, (0, 255, 255), -1) # Center point
# C. Plotting Area (Right)
graphs_x_start = LEFT_PANE_WIDTH # Plotting area starts to the right of the left video pane
graphs_y_start = INFO_DISPLAY_HEIGHT # Plotting area aligns with the top of the video pane, below info area
# Draw Displacement Graph
draw_graph(display_canvas, time_points, displacement_points, (0, 255, 0),
f"Detected Relative Displacement (Arbitrary Units)",
disp_min_val, disp_max_val,
graphs_x_start, graphs_y_start, RIGHT_PANE_WIDTH, GRAPH_HEIGHT)
# Draw Velocity Graph (below the displacement graph)
draw_graph(display_canvas, time_points, velocity_points, (0, 255, 255),
"Detected Velocity (Flow Units/frame)",
vel_min_val, vel_max_val,
graphs_x_start, graphs_y_start + GRAPH_HEIGHT, RIGHT_PANE_WIDTH, GRAPH_HEIGHT)
cv2.imshow('Michelson Interferometer Video Analysis', display_canvas)
key = cv2.waitKey(1) & 0xFF # Adjust delay as appropriate, 1ms is near real-time
if key == ord('q'):
break
end_time = time.time()
total_time = end_time - start_time
if total_time > 0:
print(f"Analysis complete. Total frames: {frame_num}, Total time: {total_time:.2f}s, Average FPS: {frame_num / total_time:.2f}")
else:
print(f"Analysis complete. Total frames: {frame_num}, Total time: {total_time:.2f}s")
cap.release()
cv2.destroyAllWindows()要将上面代码处理的显示结果显示在labview上面,怎么实现
最新发布