Streamlit+CV拍照应用遇DuplicateWidgetID,视频流无法显示
解决Streamlit拍照应用的视频流显示与DuplicateWidgetID问题
问题根源
- DuplicateWidgetID错误:原代码在
while循环内反复创建st.button,而Streamlit每次用户交互都会重新运行整个脚本,循环会生成多个相同ID的按钮组件,触发重复ID报错。 - 视频流无法显示:Streamlit的执行模型不允许用
while循环阻塞脚本运行,这种写法会导致页面无法响应交互,也无法正常更新视频帧。
修复后的代码
import streamlit as st import cv2 from datetime import datetime def init_camera(): # 初始化摄像头,仅在首次运行或摄像头未打开时执行 if "cap" not in st.session_state or not st.session_state.cap.isOpened(): st.session_state.cap = cv2.VideoCapture(0) if not st.session_state.cap.isOpened(): st.error("无法访问摄像头") st.session_state.camera_active = False return False st.session_state.camera_active = True return True def take_photo_callback(): # 拍照回调函数,保存当前帧并停止摄像头 if "current_frame" in st.session_state and st.session_state.camera_active: current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") file_path = f"photo_{current_time}.png" cv2.imwrite(file_path, st.session_state.current_frame) st.success(f"照片已保存为 {file_path}") st.session_state.camera_active = False st.session_state.cap.release() def main(): st.title("简易拍照应用") # 初始化会话状态 if "camera_active" not in st.session_state: st.session_state.camera_active = False if "current_frame" not in st.session_state: st.session_state.current_frame = None # 控制摄像头启停的按钮 col1, col2 = st.columns(2) with col1: if st.button("启动摄像头", key="start_cam"): if init_camera(): st.session_state.camera_active = True with col2: if st.button("拍照保存", key="take_photo", disabled=not st.session_state.camera_active): take_photo_callback() # 显示视频流 frame_placeholder = st.empty() if st.session_state.camera_active: while st.session_state.camera_active: ret, frame = st.session_state.cap.read() if ret: st.session_state.current_frame = frame frame_placeholder.image(frame, channels="BGR") else: st.error("无法捕获画面") st.session_state.camera_active = False st.session_state.cap.release() break if __name__ == "__main__": main()
关键改动说明
- 会话状态(st.session_state):用来保存摄像头对象、运行状态和当前帧,避免每次脚本重新运行都重复初始化摄像头,同时追踪交互状态。
- 组件位置固定:所有按钮都放在循环外的固定位置,每个按钮都设置唯一
key,彻底解决DuplicateWidgetID问题。 - 回调函数分离:把拍照逻辑抽成回调函数,避免在循环内处理交互,符合Streamlit的执行模型。
- 摄像头启停控制:通过按钮手动控制摄像头的启动和停止,避免脚本被循环阻塞,保证页面可以正常响应其他交互。
内容的提问来源于stack exchange,提问作者git-ruthvik
相关产品推荐
相关产品推荐

