You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Streamlit+CV拍照应用遇DuplicateWidgetID,视频流无法显示

解决Streamlit拍照应用的视频流显示与DuplicateWidgetID问题

问题根源

  1. DuplicateWidgetID错误:原代码在while循环内反复创建st.button,而Streamlit每次用户交互都会重新运行整个脚本,循环会生成多个相同ID的按钮组件,触发重复ID报错。
  2. 视频流无法显示: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:40:32