<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Yuichiro Tachibana (Tsuchiya) on Medium]]></title>
        <description><![CDATA[Stories by Yuichiro Tachibana (Tsuchiya) on Medium]]></description>
        <link>https://medium.com/@whitphx?source=rss-94247ffb9e0c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*KNSGLBX0k4mgA7DiHLCSzg.jpeg</url>
            <title>Stories by Yuichiro Tachibana (Tsuchiya) on Medium</title>
            <link>https://medium.com/@whitphx?source=rss-94247ffb9e0c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 20 Jun 2026 05:48:11 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@whitphx/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Limit concurrent execution of compute-intensive code in a Streamlit app]]></title>
            <link>https://medium.com/@whitphx/limit-concurrent-execution-of-compute-intensive-code-in-a-streamlit-app-9326bce626ae?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/9326bce626ae</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[streamlit]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Fri, 01 Mar 2024 09:22:01 GMT</pubDate>
            <atom:updated>2024-04-27T22:29:54.064Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Co-written and improved by </em><a href="https://twitter.com/multimodalart"><em>@multimodalart</em></a><em>.</em></p><p><strong>Update</strong>: <a href="https://arnaudmiribel.github.io/streamlit-extras/extras/concurrency_limiter/">concurrency_limiter in </a><a href="https://arnaudmiribel.github.io/streamlit-extras/extras/concurrency_limiter/">streamlit-extras</a> is now available for this purpose.</p><p>When you are developing a Streamlit app that includes a compute- or resource-intensive task, such as machine learning inference using a GPU, you may want to ensure that such a piece of the code only runs in a single thread (session) at a time, even when multiple users are accessing the app at the same time.</p><p>In such a case, using a <strong>lock</strong> is a solution. Streamlit creates a separate thread for each user session, so you can use a lock shared among the threads to control the execution of the compute-intensive code.</p><p>(For Gradio users: <em>queue</em> is a well-known solution for this problem in the case of Gradio.)</p><p>Here is an example of how to use a lock in a Streamlit app. st.cache_resource is used to create a global lock that is shared among the threads, and the lock is used to ensure that compute_intensive_task() is executed by only one thread at a time (if compute_intensive_task() is defined in another module, see the last section).</p><pre>import streamlit as st<br>import threading<br><br><br>def compute_intensive_task():<br>    &quot; Your compute-intensive code here &quot;<br><br><br>@st.cache_resource<br>def get_global_lock():<br>    return threading.Lock()<br><br><br>global_lock = get_global_lock()<br><br>with st.spinner(&quot;Running a compute-intensive task&quot;):<br>    with global_lock:<br>        st.write(&quot;Task started&quot;)<br>        compute_intensive_task()<br><br>st.write(&quot;Task completed&quot;)</pre><p>Note that, with this lock-based approach, the concurrency of the locked task is limited to 1 per lock, while the queue and worker pattern can be used to control the concurrency to any number.</p><p>Practically, it would be a good idea to add a button to manually trigger the compute-intensive task in such cases. Without it, the task would be executed every time a new user accesses the app or the app is reloaded, which is not efficient.</p><pre>import streamlit as st<br>import threading<br><br><br>def compute_intensive_task():<br>    &quot; Your compute-intensive code here &quot;<br><br><br>@st.cache_resource<br>def get_global_lock():<br>    return threading.Lock()<br><br><br>global_lock = get_global_lock()<br><br>if st.button(&quot;Run a compute-intensive task&quot;):<br>    with st.spinner(&quot;Running a compute-intensive task&quot;):<br>        with global_lock:<br>            st.write(&quot;Task started&quot;)<br>            compute_intensive_task()<br><br>    st.write(&quot;Task completed&quot;)py</pre><p>If you want something like a “concurrency group”, you can create multiple locks and use them in the same way as below. To do so, get_global_lock() is changed to take a key argument so it returns different locks for different keys, as the st.cache_resource decorator controls the cache with the func arguments (and the func’s source code).</p><pre>import streamlit as st<br>import threading<br><br><br>@st.cache_resource<br>def get_global_lock(key):<br>    return threading.Lock()<br><br><br>global_lock_A = get_global_lock(&quot;A&quot;)<br><br>with st.spinner(&quot;Running a compute-intensive task A&quot;):<br>    with global_lock_A:<br>        st.write(&quot;Task A started&quot;)<br>        compute_intensive_task_A()<br><br>global_lock_B = get_global_lock(&quot;B&quot;)<br><br>with st.spinner(&quot;Running a compute-intensive task B&quot;):<br>    with global_lock_B:<br>        st.write(&quot;Task B started&quot;)<br>        compute_intensive_task_B()<br><br>st.write(&quot;Task completed&quot;)</pre><p>If the task is defined in another module and the Streamlit app script imports it, you can define the lock object in the module scope and simply use it in the function. This lock object will be shared among the threads. This is because, while Streamlit runs the main app script in a separate namespace in a different thread for each user session, the imported modules are loaded only once and shared among the threads like normal Python module usage.</p><pre># app.py (main Streamlit app script)<br>import streamlit as st<br><br>from foo import compute_intensive_task<br><br><br>with st.spinner(&quot;Running a compute-intensive task&quot;):<br>    compute_intensive_task()<br><br>st.write(&quot;Task completed&quot;)</pre><pre># foo.py (module containing the compute-intensive task)<br>import threading<br><br><br>lock = threading.Lock()<br><br><br>def compute_intensive_task():<br>    with lock:<br>        &quot; Your compute-intensive code here &quot;</pre><p><em>Originally published at </em><a href="https://www.whitphx.info/posts/20240227-streamlit-single-concurrency-control/"><em>https://www.whitphx.info</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9326bce626ae" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Are multiple submissions to PyCon (or tech conferences) OK?]]></title>
            <link>https://medium.com/@whitphx/are-multiple-submissions-to-pycon-or-tech-conferences-ok-bd1522e0a949?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd1522e0a949</guid>
            <category><![CDATA[pycon]]></category>
            <category><![CDATA[tech]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Tue, 24 Oct 2023 15:51:55 GMT</pubDate>
            <atom:updated>2023-10-24T15:51:55.691Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>YES✅</strong></p><h3>Different from academia</h3><p><strong>Unlike academic conferences and journals</strong>, it is common to submit the same talk proposal to multiple PyCon held in different countries/places <strong>simultaneously</strong>.</p><p>For other tech conferences, I have neither participated in nor submitted a talk proposal, so I don’t know the situation. You should check the conference’s website or ask the organizer or experienced people around you.</p><h3>My case</h3><p>In 2022, I presented a talk titled “Real-time browser-ready computer vision apps with Streamlit” at <a href="https://ep2022.europython.eu/">EuroPython 2022</a> and <a href="https://tw.pycon.org/2022/">PyCon APAC 2022</a>. This time, I first submitted the talk proposal to <a href="https://us.pycon.org/2022/">PyCon US 2022</a>, then before the result came out, I submitted the same talk proposal to EuroPython and PyCon APAC as well. Because it was my first time to submit a talk proposal to PyCon, I was not sure if it was OK to submit the same talk proposal to multiple PyCon, so I asked the EuroPython and PyCon APAC organizers via e-mails. <strong>Then they said it was OK</strong>.</p><p>After I entered the PyCon sphere, I found that <strong>it is actually common</strong>. Many people do this, and it is sometimes a good strategy for them to make travel to many countries! (See my related post👉 <a href="https://www.whitphx.info/posts/20230511-conference-driven-travels/">Conf-driven Traveling✈️</a>).</p><p>I wrote this post because I couldn’t find any information clearly answering this question on the Internet.</p><p>In 2023, I actually submitted the same talk proposal to multiple PyCon and it was accepted by <a href="https://2023.pycon.de/program/GYEZVW/">PyCon DE &amp; PyData Berlin 2023</a>, <a href="https://pretalx.com/pycon-lt-2023/talk/3BQCJW/">PyCon LT</a>, <a href="https://tw.pycon.org/2023/en-us/conference/talk/301">PyCon TW</a>, and <a href="https://2023-apac.pycon.jp/timetable?id=YTGKPT">PyCon APAC</a>🎉</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd1522e0a949" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Conf-driven Traveling✈️]]></title>
            <link>https://medium.com/@whitphx/conf-driven-travels-%EF%B8%8F-9336abfd1fd8?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/9336abfd1fd8</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[life-hacking]]></category>
            <category><![CDATA[travel]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Thu, 11 May 2023 10:43:52 GMT</pubDate>
            <atom:updated>2023-05-11T10:46:34.796Z</atom:updated>
            <content:encoded><![CDATA[<h4>Sign-up first, plan second.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*M2oVI-Fp6mQFgoEE" /><figcaption>Photo by <a href="https://unsplash.com/@anete_lusina?utm_source=medium&amp;utm_medium=referral">Anete Lūsiņa</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Sign up for a conference, then plan the trip</h3><p>If you are a tech person such as a programmer, and if you like to go somewhere but you always hesitate planning trips, why not sign up for a programming conference first? You will get a huge driver to travel there. No need to choose where to go. Just search a conference that will be held soon, or whose CfP is open if you want to attend it as a speaker. You will have excellent experiences wherever you go.</p><h3>My case</h3><p>I often take part in PyCons (Python conferences).</p><p>Last year, I visited Dublin for <a href="https://ep2022.europython.eu/">EuroPython 2022</a> (<a href="https://www.whitphx.info/posts/20220717-talk-at-euro-python-2022/">this article</a>) and traveled UK and Germany after that.</p><p>At this very moment when I’m writing this, I am traveling European cities starting from Berlin for <a href="https://2023.pycon.de/">PyCon DE &amp; PyData Berlin 2023</a> and it will end in Vilnius where I will attend <a href="https://pycon.lt/2023">PyCon LT 2023</a>. I’m visiting several cities in 4 weeks between the conferences, sometimes exploring the cities and sometimes working remotely.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ASpLkc-Rn9Uuph9uvUV_yA.jpeg" /><figcaption><em>I had a presentation at PyCon DE 2023.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qMK65MTA6_ZpCtzW" /><figcaption><em>Then traveled around European cities.</em></figcaption></figure><p>Unlike academic conferences, there are normally no restrictions on posting a proposal about the same topic to multiple Python conferences. So I have submitted the proposal to other PyCons, then I will have more chances to travel if it is accepted!</p><h3>Bonuses🎉</h3><p>There are some bonuses if you take this strategy.</p><ul><li>You will have local friends at the conference.<br> Many conferences have social events where attendees can meet each other. You can also talk to speakers after their presentations, chat with other attendees at lunch or coffee break, or attend sponsors’ booths. If you would attend the conference as a (beginner) speaker, joining a mentorship program or speaker workshops is also a good chance to meet the community members.<br> At the first place, building the community is one main goal of the conference of course. For travelers, in addition, making local friends is a good way to get to know the city and get involved into there. Going local restaurants or bars with them is a wonderful experience.</li><li>Some conferences provide Financial Aid Programs. It will cover (some amount of) your travel expenses, accommodation, and/or a ticket. If you find a conference to go, check if it has the program.</li><li>In most conferences, the ticket includes lunch and coffee break. You don’t have to worry about where to eat. You can focus on the conference and enjoy the lunch with other attendees. In addition, if you are a speaker, most conferences will provide a free ticket.</li></ul><h3>Wrap up</h3><p>What you have to do is simple.</p><ol><li>Search a conference that will be held soon, or whose CfP is open.</li><li>Buy a ticket or submit a proposal.</li></ol><p>Then do all the remaining stuff automatically, such as booking the flight and the hotel!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9336abfd1fd8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Accessible color themes for Streamlit apps]]></title>
            <link>https://blog.streamlit.io/accessible-color-themes-for-streamlit-apps-6be3123a169e?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/6be3123a169e</guid>
            <category><![CDATA[web-design]]></category>
            <category><![CDATA[streamlit]]></category>
            <category><![CDATA[color-scheme]]></category>
            <category><![CDATA[web-accessibility]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Fri, 05 May 2023 23:22:25 GMT</pubDate>
            <atom:updated>2023-05-05T23:22:25.019Z</atom:updated>
            <content:encoded><![CDATA[<h4>Control your app’s color scheme and visual accessibility</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*oCtOMedkcqLKSMw84YH4Mg@2x.png" /></figure><h3>Control your app’s color scheme and visual accessibility</h3><p>One great feature of Streamlit is theming, so you can control your app’s color scheme. But creating a new color scheme can be hard. And making it visually accessible can be even harder.</p><p>So I made a color theme editor app with four color parameters of the Streamlit themes which general-purpose color editors/generators don’t even consider: primaryColor, backgroundColor, secondaryBackgroundColor, and textColor. I also followed the color contrast guidelines from the <a href="https://www.w3.org/TR/WCAG20/">Web Content Accessibility Guidelines (WCAG) 2.0 standard</a> for the app’s visual accessibility.</p><p>In this post, I’ll show you how to build this app in eight steps:</p><ol><li>Define preset colors</li><li>If set, load the theme config</li><li>Manage the edited color parameters in the session state</li><li>Set colors programmatically</li><li>Define a composed widget of a color picker and a slider</li><li>Use WCAG contrast table</li><li>Show generated configs</li><li>Apply the edited theme to the app</li></ol><p>👉 You can assess the app <a href="https://editcolor.streamlit.app/">here</a> and the source code <a href="https://github.com/whitphx/streamlit-theme-editor">here</a>. This article is based on the revision [7213dbb]( 7213dbb).</p><h3>5 key components of the color theme editor app</h3><p>The app consists of five components to help you create and test accessible color themes:</p><ul><li><strong>“Generate a random color scheme” button.</strong> Click this button to update the color theme with randomly selected colors that maintain high contrast between foreground and background.</li><li><strong>Color pickers.</strong> Use these components to adjust colors and their luminance parameters. Use the luminance slider to adjust contrast while preserving the color’s aesthetic.</li><li><strong>WCAG contrast ratio table.</strong> Display a 2x2 matrix of contrast ratios for the selected colors and the corresponding WCAG 2.0 level (AA or AAA) for each ratio to make your color theme accessible.</li><li><strong>Config generator.</strong> Generate content for the <strong>.streamlit/config.toml</strong> config file and a shell command with color theme arguments. Just copy/paste it into your project to apply the edited theme.</li><li><strong>“Apply theme on this page” checkbox.</strong> Check this box to apply the color theme you are editing to the whole application, letting you preview the colors in a real Streamlit app and synchronize the changes.</li></ul><h3>Step 1. Define preset colors</h3><p>The app has preset colors that I picked from the Streamlit source code below:</p><ul><li><a href="https://github.com/streamlit/streamlit/blob/1.19.0/frontend/src/theme/baseTheme/themeColors.ts">Base theme</a></li><li><a href="https://github.com/streamlit/streamlit/blob/1.19.0/frontend/src/theme/lightTheme/themeColors.ts">Light theme</a></li><li><a href="https://github.com/streamlit/streamlit/blob/1.19.0/frontend/src/theme/darkTheme/themeColors.ts">Dark theme</a></li></ul><pre>preset_colors: list[tuple[str, ThemeColor]] = [<br>    (&quot;Default light&quot;, ThemeColor(<br>            primaryColor=&quot;#ff4b4b&quot;,<br>            backgroundColor=&quot;#ffffff&quot;,<br>            secondaryBackgroundColor=&quot;#f0f2f6&quot;,<br>            textColor=&quot;#31333F&quot;,<br>        )),<br>    (&quot;Default dark&quot;, ThemeColor(<br>            primaryColor=&quot;#ff4b4b&quot;,<br>            backgroundColor=&quot;#0e1117&quot;,<br>            secondaryBackgroundColor=&quot;#262730&quot;,<br>            textColor=&quot;#fafafa&quot;,<br>    ))<br>]</pre><h3>Step 2. If set, load the theme config</h3><p>This is a bit of a hack. You can access the global config object via st._config to get the theme config from it and to use it as a preset color:</p><pre>@st.cache_resource<br>def get_config_theme_color():<br>    config_theme_primaryColor = st._config.get_option(&#39;theme.primaryColor&#39;)<br>    config_theme_backgroundColor = st._config.get_option(&#39;theme.backgroundColor&#39;)<br>    config_theme_secondaryBackgroundColor = st._config.get_option(&#39;theme.secondaryBackgroundColor&#39;)<br>    config_theme_textColor = st._config.get_option(&#39;theme.textColor&#39;)<br>    if config_theme_primaryColor and config_theme_backgroundColor and config_theme_secondaryBackgroundColor and config_theme_textColor:<br>        return ThemeColor(<br>            primaryColor=config_theme_primaryColor,<br>            backgroundColor=config_theme_backgroundColor,<br>            secondaryBackgroundColor=config_theme_secondaryBackgroundColor,<br>            textColor=config_theme_textColor,<br>        )<br><br>return None</pre><pre>theme_from_initial_config = util.get_config_theme_color()<br>if theme_from_initial_config:<br>    preset_colors.append((&quot;From the config&quot;, theme_from_initial_config))</pre><h3>Step 3. Manage the edited color parameters in the session state</h3><p>Store the RGB color values in the session state using the same keys as the color picker widgets. This lets you programmatically set the color picker values. It’s useful when updating them with new values from the random color generator. These values are synced with the color picker widgets, so their values must be compatible with the color picker’s #RRGGBB format.</p><p>Aside from managing RGB values, you also maintain the HSL values in the session state to control the slider widgets. Using the HSL format makes it easier to edit colors in a more intuitive way. To synchronize these two formats, create a utility function named set_color and use it every time you update the color values:</p><pre>def sync_rgb_to_hls(key: str):<br>    # HLS states are necessary for the HLS sliders.<br>    rgb = util.parse_hex(st.session_state[key])<br>    hls = colorsys.rgb_to_hls(rgb[0], rgb[1], rgb[2])<br>    st.session_state[f&quot;{key}H&quot;] = round(hls[0] * 360)<br>    st.session_state[f&quot;{key}L&quot;] = round(hls[1] * 100)<br>    st.session_state[f&quot;{key}S&quot;] = round(hls[2] * 100)<br><br><br>def set_color(key: str, color: str):<br>    st.session_state[key] = color<br>    sync_rgb_to_hls(key)<br><br><br>if &#39;preset_color&#39; not in st.session_state or &#39;backgroundColor&#39; not in st.session_state or &#39;secondaryBackgroundColor&#39; not in st.session_state or &#39;textColor&#39; not in st.session_state:<br>    set_color(&#39;primaryColor&#39;, default_color.primaryColor)<br>    set_color(&#39;backgroundColor&#39;, default_color.backgroundColor)<br>    set_color(&#39;secondaryBackgroundColor&#39;, default_color.secondaryBackgroundColor)<br>    set_color(&#39;textColor&#39;, default_color.textColor)</pre><h3>Step 4. Set colors programmatically</h3><p>To set the currently edited colors from the selectbox widget with the preset colors, use set_color():</p><pre>def on_preset_color_selected():<br>    _, color = preset_colors[st.session_state.preset_color]<br>    set_color(&#39;primaryColor&#39;, color.primaryColor)<br>    set_color(&#39;backgroundColor&#39;, color.backgroundColor)<br>    set_color(&#39;secondaryBackgroundColor&#39;, color.secondaryBackgroundColor)<br>    set_color(&#39;textColor&#39;, color.textColor)<br><br><br>st.selectbox(&quot;Preset colors&quot;, key=&quot;preset_color&quot;, options=range(len(preset_colors)), format_func=lambda idx: preset_colors[idx][0], on_change=on_preset_color_selected)</pre><p>You can also implement a random color generator button:</p><pre>if st.button(&quot;🎨 Generate a random color scheme 🎲&quot;):<br>    primary_color, text_color, basic_background, secondary_background = util.generate_color_scheme()<br>    set_color(&#39;primaryColor&#39;, primary_color)<br>    set_color(&#39;backgroundColor&#39;, basic_background)<br>    set_color(&#39;secondaryBackgroundColor&#39;, secondary_background)<br>    set_color(&#39;textColor&#39;, text_color)</pre><p>To implement util.generate_color_scheme, refer to <a href="https://github.com/whitphx/streamlit-theme-editor/blob/7213dbb379b63f4d97ed0da31765ee12773c3696/util.py#L77-L84">this code</a>. It generates colors with the constraint that the luminance (the &quot;L&quot; in the HSL format) parameters of the colors have enough difference.</p><h3>Step 5. Define a composed widget of a color picker and a slider</h3><p>This app provides a pair of a color picker and a slider to control the color’s luminance parameter (to adjust the color contrast while maintaining its original appearance). There are four pairs of components. Each defined by a function called color_picker to make it reusable.</p><p>One trick here is to set the on_change callback of each component to synchronize the RGB data and the HSL data in the session state:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/579/1*9cSn0DJimvC0piNH7iSFOw@2x.png" /></figure><pre>def color_picker(label: str, key: str, default_color: str) -&gt; None:<br>    col1, col2 = st.columns([1, 3])<br>    with col1:<br>        color = st.color_picker(label, key=key, on_change=sync_rgb_to_hls, kwargs={&quot;key&quot;: key})<br>    with col2:<br>        r,g,b = util.parse_hex(default_color)<br>        h,l,s = colorsys.rgb_to_hls(r,g,b)<br>        if f&quot;{key}H&quot; not in st.session_state:<br>            st.session_state[f&quot;{key}H&quot;] = round(h * 360)<br><br>        st.slider(f&quot;L for {label}&quot;, key=f&quot;{key}L&quot;, min_value=0, max_value=100, value=round(l * 100), format=&quot;%d%%&quot;, label_visibility=&quot;collapsed&quot;, on_change=sync_hls_to_rgb, kwargs={&quot;key&quot;: key})<br><br>        if f&quot;{key}S&quot; not in st.session_state:<br>            st.session_state[f&quot;{key}S&quot;] = round(s * 100)<br><br>    return color</pre><pre>primary_color = color_picker(&#39;Primary color&#39;, key=&quot;primaryColor&quot;, default_color=default_color.primaryColor)<br>text_color = color_picker(&#39;Text color&#39;, key=&quot;textColor&quot;, default_color=default_color.textColor)<br>background_color = color_picker(&#39;Background color&#39;, key=&quot;backgroundColor&quot;, default_color=default_color.backgroundColor)<br>secondary_background_color = color_picker(&#39;Secondary background color&#39;, key=&quot;secondaryBackgroundColor&quot;, default_color=default_color.secondaryBackgroundColor)</pre><h3>Step 6. Use WCAG contrast table</h3><p>This table layout is created using stacked st.column(). The content of each cell is encapsulated within a reusable helper function, such as synced_color_picker or fragments.contrast_summary:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BYWx2glk1dcNOvjBLVcliQ.png" /></figure><pre>col1, col2, col3 = st.columns(3)<br>with col2:<br>    synced_color_picker(&quot;Background color&quot;, value=background_color, key=&quot;backgroundColor&quot;)<br>with col3:<br>    synced_color_picker(&quot;Secondary background color&quot;, value=secondary_background_color, key=&quot;secondaryBackgroundColor&quot;)<br><br>col1, col2, col3 = st.columns(3)<br>with col1:<br>    synced_color_picker(&quot;Primary color&quot;, value=primary_color, key=&quot;primaryColor&quot;)<br>with col2:<br>    fragments.contrast_summary(&quot;Primary/Background&quot;, primary_color, background_color)<br>with col3:<br>    fragments.contrast_summary(&quot;Primary/Secondary background&quot;, primary_color, secondary_background_color)<br><br>col1, col2, col3 = st.columns(3)<br>with col1:<br>    synced_color_picker(&quot;Text color&quot;, value=text_color, key=&quot;textColor&quot;)<br>with col2:<br>    fragments.contrast_summary(&quot;Text/Background&quot;, text_color, background_color)<br>with col3:<br>    fragments.contrast_summary(&quot;Text/Secondary background&quot;, text_color, secondary_background_color)</pre><p>synced_color_picker() is just a color picker component, but its value is synchronized with the relevant color picker component that appeared above. Its value is also managed in the session state with the value argument and the on_change callback:</p><pre>def synced_color_picker(label: str, value: str, key: str):<br>    def on_change():<br>        st.session_state[key] = st.session_state[key + &quot;2&quot;]<br>        sync_rgb_to_hls(key)<br>    st.color_picker(label, value=value, key=key + &quot;2&quot;, on_change=on_change)</pre><p>fragments.contrast_summary() renders the WCAG contrast info. See <a href="https://github.com/whitphx/streamlit-theme-editor/blob/7213dbb379b63f4d97ed0da31765ee12773c3696/fragments.py#L7-L22">this code</a> for its implementation.</p><h3>Step 7. Show generated configs</h3><p>The app shows these ready-to-use code snippets and is done with st.code():</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/603/1*bERlgam8z0qwjO9JX7nYVA@2x.png" /></figure><pre>st.subheader(&quot;Config file (`.streamlit/config.toml`)&quot;)<br>st.code(f&quot;&quot;&quot;<br>[theme]<br>primaryColor=&quot;{primary_color}&quot;<br>backgroundColor=&quot;{background_color}&quot;<br>secondaryBackgroundColor=&quot;{secondary_background_color}&quot;<br>textColor=&quot;{text_color}&quot;<br>&quot;&quot;&quot;, language=&quot;toml&quot;)<br><br>st.subheader(&quot;Command line argument&quot;)<br>st.code(f&quot;&quot;&quot;<br>streamlit run app.py \\<br>    --theme.primaryColor=&quot;{primary_color}&quot; \\<br>    --theme.backgroundColor=&quot;{background_color}&quot; \\<br>    --theme.secondaryBackgroundColor=&quot;{secondary_background_color}&quot; \\<br>    --theme.textColor=&quot;{text_color}&quot;<br>&quot;&quot;&quot;)</pre><h3>Step 8. Apply the edited theme to the app</h3><p>For reviewing purposes, this app can apply the currently edited theme to itself. You can do it with the st._config object, as shown below:</p><pre>if st.checkbox(&quot;Apply theme to this page&quot;):<br>    st.info(&quot;Select &#39;Custom Theme&#39; in the settings dialog to see the effect&quot;)<br><br>    def reconcile_theme_config():<br>        keys = [&#39;primaryColor&#39;, &#39;backgroundColor&#39;, &#39;secondaryBackgroundColor&#39;, &#39;textColor&#39;]<br>        has_changed = False<br>        for key in keys:<br>            if st._config.get_option(f&#39;theme.{key}&#39;) != st.session_state[key]:<br>                st._config.set_option(f&#39;theme.{key}&#39;, st.session_state[key])<br>                has_changed = True<br>        if has_changed:<br>            st.experimental_rerun()<br><br>    reconcile_theme_config()<br><br>    fragments.sample_components(&quot;body&quot;)<br>    with st.sidebar:<br>        fragments.sample_components(&quot;sidebar&quot;)</pre><p>Use st._config.set_option() to update the configuration values. Reload the app for the changes to take effect by using st.experimental_rerun(). It was inspired by <a href="https://github.com/jrieke/streamlit-theme-generator">jrieke/streamlit-theme-generator</a> (here is the <a href="https://github.com/jrieke/streamlit-theme-generator/blob/79861b81e415a97590e69d4a0a2efe624a91ad0b/streamlit_app.py?ref=blog.streamlit.io#L86-L99">code</a>).</p><h3>Wrapping up</h3><p>The Streamlit color theme editor app offers a simple and effective solution for creating visually appealing and accessible color themes for your apps. Thanks to the WCAG 2.0 standard and the real-time preview, your themes will now be attractive and accessible to all users.</p><p>Happy app-building! 🧑‍💻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6be3123a169e" width="1" height="1" alt=""><hr><p><a href="https://blog.streamlit.io/accessible-color-themes-for-streamlit-apps-6be3123a169e">Accessible color themes for Streamlit apps</a> was originally published in <a href="https://blog.streamlit.io">Streamlit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlit meets WebAssembly — stlite]]></title>
            <link>https://medium.com/@whitphx/streamlit-meets-webassembly-stlite-213786afe2a0?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/213786afe2a0</guid>
            <category><![CDATA[pyodide]]></category>
            <category><![CDATA[streamlit]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[webassembly]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Mon, 07 Nov 2022 17:08:47 GMT</pubDate>
            <atom:updated>2022-11-16T12:00:43.890Z</atom:updated>
            <content:encoded><![CDATA[<h3>Streamlit meets WebAssembly — stlite</h3><h4>Streamlit apps can run completely on web browsers without the server-side Python runtime</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tuDurkQALVCu3n867b-qqQ.jpeg" /></figure><h3>TL;DR</h3><ul><li>I created a <strong>WebAssembly</strong> (<strong>Wasm</strong>) port of <strong>Streamlit</strong>, ”<em>stlite</em>“.</li><li>You can try it out and share your apps on ”<a href="https://edit.share.stlite.net/"><strong>stlite sharing</strong></a>”, the online code editor + app sharing platform for <em>stlite</em>.</li><li>The Wasm-based runtime provides many benefits such as offline capability, data privacy, and scalability.</li><li>In addition to <a href="https://edit.share.stlite.net/">stlite sharing</a>, you can also host your Streamlit/stlite apps <a href="https://github.com/whitphx/stlite#use-stlite-on-your-web-page">on your web site</a> or create <a href="https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme">offline desktop applications</a>. So you can create multi-platform apps only with Python coding.</li></ul><h3>Streamlit: a great Python web app framework</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qq2TRBTJyPNaq_blmvWWZA.png" /></figure><p><a href="https://streamlit.io/">Streamlit</a> is a web app framework with which we can create interactive web apps only with Python coding. It is especially famous and popular for data apps development as it well supports many data widgets cooperating the DS/ML Python ecosystem, while it also covers a wide range of general purpose web development.</p><p>When you run a Streamlit app, the Streamlit framework starts a web server and it serves a frontend app that is a single page application (SPA) whose contents are dynamically constructed according to your app script written in Python. Then the frontend app continuously communicates with the web server and triggers the Python code executions on the server upon some events to get some results that dynamically update the frontend view. Due to this unique server-client architecture, we can construct interactive web apps only by writing the logics in the server-side Python code.</p><figure><img alt="Streamlit architecture" src="https://cdn-images-1.medium.com/max/1024/1*A36VWpApHUiUUd3jbypRPg.png" /><figcaption>Streamlit architecture</figcaption></figure><h3>stlite: client-side Streamlit powered by Pyodide</h3><p>There had been an epoch-making invent in the Python world — <a href="https://pyodide.org/">Pyodide</a>. It is a CPython runtime compiled for WebAssembly (Wasm) that runs on web browsers (and NodeJS).</p><figure><img alt="Pyodide logo" src="https://cdn-images-1.medium.com/max/700/0*NEH_sOn-iPMP0cEe.png" /></figure><p>So I customized Streamlit to run on Pyodide runtime and released it as ”<a href="https://github.com/whitphx/stlite">stlite</a>“!</p><figure><img alt="stlite logo" src="https://cdn-images-1.medium.com/max/540/1*gibjgKyHPPxVp1VmoHdzeg.png" /></figure><p>On <em>stlite</em>, the entry point is a JavaScript file that mounts the Streamlit frontend SPA into the HTML page, loads the Pyodide environment, and launches the Streamlit Python server in the Pyodide environment <strong>on the web browser</strong>.</p><p>With this architecture, thanks to Pyodide, the Python runtime no longer exists on the server side since it runs on the web browser. The web server is only for serving the static files such as HTML, JS, and CSS.</p><figure><img alt="stlite architecture" src="https://cdn-images-1.medium.com/max/1024/1*0r0aN0fVlzWurBgOfaYC-Q.png" /><figcaption>stlite architecture</figcaption></figure><h3>Pros and Cons</h3><p>As it runs completely on the browsers, <em>stlite</em> has some benefits that the original Streamlit does not have.</p><ul><li><strong>Offline capability</strong>: As even the Streamlit “server” runs on the browser, all the components resides on the client side, so once all the resources are loaded from the web server, the app can run offline.</li><li><strong>Data privacy</strong>: Since the entire app runs on your browser, even when you do “upload” some files from the file uploader on the page, these files are NEVER sent to any remote servers and only processed within your machine.<br>This feature is sometimes beneficial especially in the applications of data science, machine learning, or statistics where Streamlit is widely used, as these often have strict privacy rules.</li><li><strong>Scalability</strong>: The main workload such as machine learning computation written in Python moves from the server to each browser, so the system becomes scalable.</li><li><strong>Multi-platform (web, desktop, mobile)</strong>: As it runs on web browsers, it can also be an installable app (<a href="https://web.dev/progressive-web-apps/">PWA</a>), and can be bundled into a desktop app with Electron. Building mobile apps is also on the read map.<br>As a result, you can create interactive apps only in Python with Streamlit and bundle them for each platform.</li><li><strong>Online editing experience</strong>: I developed the online editor &amp; real-time preview service for Streamlit apps based on <em>stlite</em> — <strong>stlite sharing</strong> that we will see below soon.<br>Precisely, this is not purely because of WebAssembly, but the Wasm-based architecture made it easier to create such a service which could not have existed before.</li></ul><p>On the other hand, <em>stlite</em> and Pyodide has some disadvantages as a tradeoff.</p><ul><li><strong>Some packages are not available</strong>: Some C extension packages such as TensorFlow cannot be installed because C extensions must be compiled for the Pyodide runtime specifically. For more details, read the Pyodide articles such as <a href="https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel">this one</a>.</li><li><strong>Large initial payload</strong>: A large amount of resources will be downloaded when the user opens the app because Pyodide loads the whole Python runtime and the standard libraries, and <em>stlite</em> also downloads the necessary wheel files including the streamlit package.</li><li><strong>Network restriction</strong>: For the security reasons, accessing remote resources from the <em>stlite</em> applications are restricted by the browser, e.g. CORS.</li><li><strong>The source code is open</strong>: Note that all the source code and hosted data are sent to the client side, so they are visible to the users. You must not put any secrets on the source code of the <em>stlite</em> apps.</li></ul><p>(This section is highly inspired by <a href="https://shiny.rstudio.com/py/docs/shinylive.html">the blog post about Shinylive</a>.)</p><h3>Online code editor + app sharing platform: stlite sharing</h3><p><a href="https://edit.share.stlite.net/"><strong>stlite sharing</strong></a> is an online code editor + app sharing platform for <em>stlite</em>. It is the easiest and fastest way to try out <em>stlite</em>.</p><figure><img alt="stlite sharing hero image" src="https://cdn-images-1.medium.com/max/1024/0*7-0HwGPnkEX_LQs-.png" /></figure><p>The following screenshot is the online editing &amp; app preview mode of <strong>stlite sharing</strong>. You can try it out at <a href="https://edit.share.stlite.net/">https://edit.share.stlite.net/</a> .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Iym3rc2mIQ_Rp261AjygIA.png" /></figure><p>When you create/edit/delete the files on the left pane, those changes are applied to the virtual file system on top of which <em>stlite</em> runs, then the Streamlit server detects the file changes and recommends you to hot-reload the application, as shown in the screenshot below. (The file change detection and hot-reloading is a part of the Streamlit core features. If you are not familiar with it, read <a href="https://docs.streamlit.io/library/get-started/main-concepts#development-flow">the official document</a>.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8Kk2mlolFVVyXWBGUP4Yqg.png" /></figure><p>The file changes are saved to the file system when the “Save” button is clicked. You can also add or delete the files on the file tabs.</p><h3>Shareable URLs</h3><p>On <em>stlite sharing</em>, you can get the shareable URL of the current app displayed at top right. There is also the “Open App” link next to it, through which you can open the URL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DLaU9sVYUCgwdt2hL2Fk3w.png" /></figure><p>By navigating to the URL or clicking the link, the app opens in the sharing mode as below, where only the Streamlit app opens and there are not the editor, the sharing link, and any other widgets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Ry_4ZeuTyzixNW18mHWvA.png" /></figure><p>Its URL is https://share.stlite.net/ with a long hash like #!ChBz....</p><p>The host https://share.stlite.net/ is for the app sharing mode of <em>stlite sharing</em>, while the editor mode we have seen above is at <a href="https://edit.share.stlite.net/.">https://edit.share.stlite.net/.</a></p><p>In the long hash part of the URL like #!ChBz..., all the source code and data edited on the editor are encoded and embedded, so you can share the app only by copying this URL.</p><p>Another good point of this approach is, since all the code and data are embedded into the URL hash, these data are <a href="https://en.wikipedia.org/wiki/URI_fragment#Basics">never uploaded and saved to the remote server</a>.</p><p>The encoded URL hash is also loadable on the editor mode, so if you open the editor mode URL, <a href="https://edit.share.stlite.net/">https://<strong>edit</strong>.share.stlite.net/</a> with the hash, the app data will be restored on the editor.</p><p>Note that some SNS and URL shortening services cut off the long URLs, so you should take care of it when sharing the URL on such platforms. For the details, read <a href="https://shiny.rstudio.com/py/docs/shinylive.html#sharing-shinylive-applications">this part of the Shinylive article</a>.</p><p>This URL mechanism is inspired by <a href="https://shiny.rstudio.com/py/docs/shinylive.html">Shinylive</a>.</p><h3>Adding files</h3><p>By clicking the ”+” button at the file tabs area, you can create a new file.</p><p>You can also upload files from your local env (the files are never “uploaded” to any remote servers. They are just sent to the virtual file system on your browser.) Though this uploader, <strong>you can also add binary files</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rCuQvjTpMwtEzv0She--Rw.png" /></figure><h3>Files in directories and Multipage apps support</h3><p>You can edit the file name on the tab, and importantly, by inserting the file path delimiter, /, you can create files in directories.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*198e_t8cMZ9icg2kmmw1jw.png" /></figure><p>With this feature, <em>stlite sharing</em> supports the Streamlit’s <strong>Multipage apps</strong>. In short, by creating a Python file in the pages/ directory (pages/*.py), you can add a new page in the app. For the details, read the <a href="https://docs.streamlit.io/library/get-started/multipage-apps">official document about Multipage apps</a>.</p><h3>Installing packages</h3><p>When you need to install some packages, use the special “requirements” tab.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5aiaC56VF5iQyMeII5VFQQ.png" /></figure><p>The package names written in the editor on this tab will be passed to the package installer <a href="https://pyodide.org/en/stable/usage/api/micropip-api.html#micropip.install">micropip.install()</a> internally (on the Pyodide runtime, <a href="https://pyodide.org/en/stable/usage/api/micropip-api.html">micropip</a> is used as a package manager) when clicking the “Save” button. Write one package name per line.</p><p>This is a special tab in that the file content is not saved into the file system. It is used only for specifying the package names passed to micropip.install().</p><p>Please note again that some C extension packages cannot be installed. The following screenshot is the sample of such a case, where installing a C extension failed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rwDTqfLJfeSa9kurg5PffQ.png" /></figure><h3>Samples</h3><p>You can find some samples on the side menu, so check out these! I hope some inspire you.</p><p>For example, the “Streamlit Hello” demo runs the official Streamlit demo on <em>stlite</em> while it is available through the streamlit hello command with the original Streamlit.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4qgBsJVDI1B_ZxwvyCHSbA.png" /></figure><p>With a recent release of Pyodide <a href="https://blog.pyodide.org/posts/0.21-release/">0.21</a>, many C-extensions became available including OpenCV. <em>stlite sharing</em> includes the samples using OpenCV and scikit-image with real-time video streaming. Even these samples run on web browsers and nothing are sent to any remote servers!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dC7Ofx-I4dOCZsmuHw47oQ.png" /></figure><h3>Yuichiro on Twitter: &quot;Client-side real-time image processing!Write the app in #Python with @Streamlit + OpenCV, scikit-image, etc.,Run it on the browser with #WebAssembly.↓Try it out on stlite sharing!https://t.co/7GH6onBgrfEverything works on the browser. No uploads! pic.twitter.com/6xUlHBBfgU / Twitter&quot;</h3><p>Client-side real-time image processing!Write the app in #Python with @Streamlit + OpenCV, scikit-image, etc.,Run it on the browser with #WebAssembly.↓Try it out on stlite sharing!https://t.co/7GH6onBgrfEverything works on the browser. No uploads! pic.twitter.com/6xUlHBBfgU</p><h3>Loading apps from GitHub or Gist</h3><p>Instead of the encoded URL hash explained above, you can host your Python script somewhere and pass its URL to <em>stlite sharing</em> via the URL hash as below.</p><pre><a href="https://share.stlite.net/#https://...py">https://share.stlite.net/#https://...py</a></pre><p>If the URL is of GitHub or Gist, you can pass the preview page URL instead of the raw file URL. For example, <a href="https://share.stlite.net/#https://github.com/napoles-uach/test/blob/main/app.py">https://share.stlite.net/#https://github.com/napoles-uach/test/blob/main/app.py</a> works.</p><p>When you need to install some packages, use the following format instead.</p><pre>https://share.stlite.net/#url=&lt;Script URL&gt;?req=&lt;Package1&gt;&amp;req=&lt;Package2&gt;</pre><p>The example of this format installing opencv-python and matplotlib follows: <a href="https://share.stlite.net/#url=https://github.com/whitphx/stlite-sample/blob/main/opencv-image-processing.py&amp;req=opencv-python&amp;req=matplotlib">https://share.stlite.net/#url=https://github.com/whitphx/stlite-sample/blob/main/opencv-image-processing.py&amp;req=opencv-python&amp;req=matplotlib</a><br>There is also the Gist version: <a href="https://share.stlite.net/#url=https://gist.github.com/whitphx/f23b7b2bbda19cd421121bd72ebf2101&amp;req=opencv-python&amp;req=matplotlib">https://share.stlite.net/#url=https://gist.github.com/whitphx/f23b7b2bbda19cd421121bd72ebf2101&amp;req=opencv-python&amp;req=matplotlib</a></p><h3>Host your Streamlit app on your web site</h3><p><em>stlite</em> also supports self-hosting the apps.</p><p>All you have to do is write and host a single HTML file.</p><p>It would be something like below, where you can embed the Python source code into the JS code as a string literal.</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;UTF-8&quot; /&gt;<br>    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;<br>    &lt;meta<br>      name=&quot;viewport&quot;<br>      content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;<br>    /&gt;<br>    &lt;title&gt;stlite app&lt;/title&gt;<br>    &lt;link<br>      rel=&quot;stylesheet&quot;<br>      href=&quot;https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.css&quot;<br>    /&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;<br>    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.js&quot;&gt;&lt;/script&gt;<br>    &lt;script&gt;<br>      stlite.mount(<br>        `<br>import streamlit as st<br>name = st.text_input(&#39;Your name&#39;)<br>st.write(&quot;Hello,&quot;, name or &quot;world&quot;)<br>`,<br>        document.getElementById(&quot;root&quot;)<br>      );<br>    &lt;/script&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>If you need to install some packages, specify the entrypoint file name, and/or use multiple files, use the following more flexible API.</p><pre>stlite.mount(<br>  {<br>    requirements: [&quot;matplotlib&quot;], // Packages to install<br>    entrypoint: &quot;streamlit_app.py&quot;, // The target file of the `streamlit run` command<br>    files: {<br>      &quot;streamlit_app.py&quot;: `<br>import streamlit as st<br>import matplotlib.pyplot as plt<br>import numpy as np<br>size = st.slider(&quot;Sample size&quot;, 100, 1000)<br>arr = np.random.normal(1, 1, size=size)<br>fig, ax = plt.subplots()<br>ax.hist(arr, bins=20)<br>st.pyplot(fig)<br>`,<br>    },<br>  },<br>  document.getElementById(&quot;root&quot;)<br>);</pre><p>After writing the HTML file, host it wherever you like, such as GitHub Pages. In your local env, you can test it with python -m http.server 8000.</p><p>This YouTube video may guide you to set it up on GitHub Pages (although the <em>stlite</em> API used in the video is out-dated now.)</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FVQdktxgbmmg%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVQdktxgbmmg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FVQdktxgbmmg%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/159f3bf3125556b3f2ed84bf60f1eb57/href">https://medium.com/media/159f3bf3125556b3f2ed84bf60f1eb57/href</a></iframe><p><strong>Please read the </strong><a href="https://github.com/whitphx/stlite#use-stlite-on-your-web-page"><strong>README</strong></a><strong> for the details about self-hosting apps!</strong></p><h3>Bundle your Streamlit app as a desktop app</h3><p>The <em>stlite</em> apps run on web browsers, so it is also possible to bundle them into desktop apps with Electron.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kSvtZg1n4XYi3_q-p-UPww.png" /></figure><h3>Charly Wargnier 🥑 on Twitter: &quot;.@whitphx, the brilliant mind behind `stlite` is getting close to having &#39;@streamlit app -&gt; desktop app&#39; working! 🎈🤯(via @blackaryz, cc: @electronjs)🔗https://t.co/RDHoVp3rhv pic.twitter.com/HMqMyH70eG / Twitter&quot;</h3><p>@whitphx, the brilliant mind behind `stlite` is getting close to having &#39;@streamlit app -&gt; desktop app&#39; working! 🎈🤯(via @blackaryz, cc: @electronjs)🔗https://t.co/RDHoVp3rhv pic.twitter.com/HMqMyH70eG</p><p>To create your own desktop app with <em>stlite</em>, <strong>follow this instruction! -&gt;</strong> <a href="https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme">https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme</a></p><p>There is a sample app repository and its distributable files too.</p><ul><li><a href="https://github.com/whitphx/stlite-desktop-example">A sample app repository</a></li><li><a href="https://github.com/whitphx/stlite-desktop-example/releases/tag/v0.2.0">Distributable files</a> (the macOS app file is not signed, so the security alert may be shown)</li></ul><h3>Tell us your story!</h3><p>When you create some apps with <em>stlite</em>, please share it!</p><p>If it’s on <strong>stlite sharing</strong>, all you have to do is copy and paste the URL 👍</p><p>These are good places to share your apps, samples, or case studies!</p><ul><li><a href="https://github.com/whitphx/stlite/discussions/categories/show-and-tell"><strong>stlite</strong> GitHub Discussions</a></li><li><a href="https://discuss.streamlit.io/">Streamlit community forum</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=213786afe2a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Developing Web-Based Real-Time Video/Audio Processing Apps Quickly with Streamlit]]></title>
            <link>https://medium.com/data-science/developing-web-based-real-time-video-audio-processing-apps-quickly-with-streamlit-7c7bcd0bc5a8?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/7c7bcd0bc5a8</guid>
            <category><![CDATA[hands-on-tutorials]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[streamlit]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[deep-dives]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Wed, 02 Feb 2022 05:36:46 GMT</pubDate>
            <atom:updated>2022-09-02T13:36:44.892Z</atom:updated>
            <content:encoded><![CDATA[<h4>In this article, we will see how we can create browser-ready real-time video/audio processing apps with Streamlit.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eGozi0vrtOKChteXMCEM6g.png" /><figcaption>Image by Author</figcaption></figure><p>Streamlit is a Python framework with which developers can quickly build web apps without frontend coding. On top of it, developers can make real-time video/audio processing apps that receive video/audio streams from users’ media devices, only with ~10 lines of code in the case of the simplest example.</p><p>Since such apps are web-based, they can be deployed to the cloud, shared with users easily, and have modern and user-friendly UIs.</p><p>This tech stack is useful for creating demos and prototyping ideas of video/audio apps such as human or object detection, style transfer, image filters, speech recognition, video chat apps, and more.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*nwr9_9Ee0TSE_vBJ.gif" /><figcaption><em>A sample web-based object detection app. Users can change the threshold interactively during execution. </em><a href="https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py"><em>Online demo🎈</em></a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*1Wuc0VtaduShJnPF.gif" /><figcaption><em>A sample web-based style transfer app. Users can change model type and model parameters interactively during execution. </em><a href="https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py"><em>Online demo🎈</em></a></figcaption></figure><p>You can see more examples at the <em>examples</em> section below.</p><p><strong>NOTE</strong>: These sample apps are hosted on the public cloud ( <a href="https://streamlit.io/cloud">Streamlit Cloud</a>), and the video and audio streams are transmitted to and processed at the cloud server. While those data are only processed on memory and not saved to any storage, however, if you are concerned, please do not use them. As for the following contents in this article, we can execute all of them on our local. In addition, you can try the examples above on your local by following the instructions at the <em>examples</em> section below.</p><p><strong>NOTE:</strong> I had a presentation about this topic at <a href="https://ep2022.europython.eu/">EuroPython 2022</a> titled as <a href="https://ep2022.europython.eu/session/real-time-browser-ready-computer-vision-apps-with-streamlit">“Real-time browser-ready computer vision apps with Streamlit.”</a> The talk video is available below:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FuVD6B8WLMTo%3Fstart%3D16118%26feature%3Doembed%26start%3D16118&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DuVD6B8WLMTo&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FuVD6B8WLMTo%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/d9a9297d9b267c3e5b247bc6cac9b968/href">https://medium.com/media/d9a9297d9b267c3e5b247bc6cac9b968/href</a></iframe><p><strong>UPDATE:</strong> This article has been updated on 2022/09/02 using the newly introduced API of streamlit-webrtc that has been available since <a href="https://github.com/whitphx/streamlit-webrtc/blob/main/CHANGELOG.md#0400---2022-06-07">v0.40.0</a>.</p><h3>The advantages of web-based apps</h3><p>We have been typically using OpenCV to build real-time demo apps of image or video processing. Some of you (especially developers or researchers in such fields) may have seen the following code or similar many times.</p><pre>import cv2<br><br>cap = cv2.VideoCapture(0)<br><br>while True:<br>    ret, frame = cap.read()<br><br>    img = cv2.Canny(frame, 100, 200)  # Some image processing<br><br>    cv2.imshow(&#39;frame&#39;, img)<br>    if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):<br>        break<br><br>cap.release()<br>cv2.destroyAllWindows()</pre><p>Compared to the GUI apps like above using cv2.VideoCapture and cv2.imshow that run on local environments, web-based apps have some advantages like below.</p><p>Easy to share and run:</p><ul><li>If we deploy the apps on the cloud, we can share the apps with our users simply by sending the URLs.</li><li>The users can use the apps only by accessing them through web browsers. It does not require any set-ups or external dependencies.</li></ul><p>Usable on smartphones:</p><ul><li>Because all the users need is web browsers, the users can use the apps on their smartphones. It’s convenient if we can show demos on such portable devices.</li></ul><p>User-friendly UIs:</p><ul><li>Developers can use text inputs, sliders, or other web-based components to accept user inputs or show data. Such web-based UIs are more friendly for users than desktop GUIs in recent days.</li></ul><h3>Tutorial</h3><p>We will create a simple web-based real-time video processing app with ~10 or 20 LoC. Please try this tutorial in an environment where a webcam and a microphone are available.</p><p>You can check the final result of this tutorial in <a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample">this repository</a>. The deployed online demo is <a href="https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py">here🎈</a></p><p>In this tutorial, we will write code in app.py. Please create an empty app.py at first.</p><pre>$ touch app.py</pre><h4>Install necessary packages</h4><p>Next, we have to install the packages necessary for this tutorial.</p><pre>$ pip install -U streamlit streamlit-webrtc opencv-python-headless</pre><ul><li>streamlit: The Streamlit main package.</li><li>streamlit-webrtc: A custom component of Streamlit which deals with real-time video and audio streams.</li><li>opencv-python-headless: OpenCV. We choose the headless version here because we will construct the UI with Streamlit.</li></ul><h4>First contact with Streamlit</h4><p>NOTE: Please skip this section if you have experience in Streamlit.</p><p>First of all, launch the Streamlit with the command below. Please run the command in the same directory to the app.py.</p><pre>$ streamlit run app.py</pre><p>After a while, the Streamlit server process will boot up. Then access <a href="http://localhost:8501">http://localhost:8501</a> to see the page like below (or it will automatically open in the browser by default). The screenshot here is in dark-mode, and if you are using light-mode, it looks different.</p><p>At this moment, there is no content on the web page because app.py is empty. We will add lines of code in the app.py for the Streamlit app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*PRka72fPP0_cpkI6.png" /></figure><p>Open the app.py with your editor and write the code below.</p><pre>import streamlit as st<br><br>st.title(&quot;My first Streamlit app&quot;)<br>st.write(&quot;Hello, world&quot;)</pre><p>When you save the file, Streamlit will detect the file change and shows the “Rerun” and “Always rerun” buttons on the top right of the screen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*DWESA0QTNPUllxPB.png" /></figure><p>Click the “Rerun” button. Then the web page is reloaded, and the page content would be like below. The web page content is generated based on the app.py code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*Bml-NxT9SGupZ91a.png" /></figure><p>If you had clicked the “Always rerun” button, the page would automatically be reloaded every time the file changed.</p><p>Note that you have to reload the page like above in the following instructions where you update the app.py.</p><p>Now we have walked through the basic development flow of Streamlit apps. You write Python code with Streamlit components like st.title() and st.write() and pass it to the streamlit run command, then Streamlit generates the corresponding frontend contents on the web page.</p><p>In the next section, we will see how to develop a real-time video processing app on top of Streamlit. Apart from that, Streamlit itself covers more use cases such as machine learning, data science, or more general purposes. For such use cases, please see <a href="https://docs.streamlit.io/library/get-started/create-an-app">the official Streamlit tutorial</a> for example.</p><h4>Introduce the real-time video/audio streaming component</h4><p>Update the app.py as below.</p><pre>import streamlit as st<br>from streamlit_webrtc import webrtc_streamer<br><br>st.title(&quot;My first Streamlit app&quot;)<br>st.write(&quot;Hello, world&quot;)<br><br>webrtc_streamer(key=&quot;example&quot;)</pre><p>We have added a single line with webrtc_streamer(). The web app would be like the screenshot below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/0*m5IsS5LrYnDnnP-X.png" /></figure><p>At the first trial, it may take some time to compile the package so that the page keeps showing the “running” message for a while after clicking the “Rerun” button. In such a case, wait for the process to finish.</p><p>Click the “START” button to start the video and audio streaming. You may be asked for permission to access the webcam and microphone at the first trial. Grant permission in that case.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*iCOeOTSsJrCA451j.gif" /></figure><p>The webrtc_streamer(key=&quot;example&quot;) above is a Streamlit component which deals with video and audio real-time I/O through web browsers. The key argument is a unique ID in the script to identify the component instance. We have set it as &quot;example&quot; here, but you can use any string for it. The component in this example only receives video and audio from the client-side webcam and microphone and outputs the raw streams. It&#39;s the most basic version of the component. We are going to enhance its functionality by adding other options in the following sections.</p><h4>Development of a real-time video processing application</h4><p>Update the app.py as follows.</p><pre>import streamlit as st<br>from streamlit_webrtc import webrtc_streamer<br>import av<br>import cv2<br><br>st.title(&quot;My first Streamlit app&quot;)<br>st.write(&quot;Hello, world&quot;)<br><br><br>def callback(frame):<br>    img = frame.to_ndarray(format=&quot;bgr24&quot;)<br><br>    img = cv2.cvtColor(cv2.Canny(img, 100, 200), cv2.COLOR_GRAY2BGR)<br><br>    return av.VideoFrame.from_ndarray(img, format=&quot;bgr24&quot;)<br><br><br>webrtc_streamer(key=&quot;example&quot;, video_frame_callback=callback)</pre><p>Try it out by clicking the “START” button like the previous section. With this new example, you can find that an image filter is applied to the video stream.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*_mC81QA0GhPLTES4.gif" /></figure><p>We have defined a callback that receives an input frame and returns an output frame. We also put image processing (edge detection in this example) code inside the callback. As a result, we have injected the image processing code into the real-time video app through the callback.</p><p>Detailed explanations about the code follow.</p><ul><li>webrtc_streamer() can take a function object through the video_frame_callback argument as a callback.</li><li>The callback receives and returns input and output image frames. These are instances of the <a href="https://pyav.org/docs/develop/api/video.html#av.video.frame.VideoFrame">VideoFrame</a> class from <a href="https://github.com/PyAV-Org/PyAV">PyAV</a>. The PyAV library is a Python binding of ffmpeg, which provides video and audio capabilities. It is installed as a dependency of streamlit-webrtc.</li><li>The argument of the callback is an image frame in the input video stream sourced from the webcam. It can be converted into a NumPy array with frame.to_ndarray().</li><li>The returned value from the callback is displayed on the screen. In the sample above, a new VideoFrame object to be returned is generated from a NumPy array, with av.VideoFrame.from_ndarray(img, format=&quot;bgr24&quot;).</li><li>Any code can be put inside the callback. In the example above, we have used an edge detection filter cv2.Canny(img, 100, 200) (and a grayscale converter cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)) as an example.</li></ul><p>Now, we have created a browser-ready real-time video processing app! We used a simple Canny edge detector in this example, and you can replace it with any image processing code in your original app.</p><p>If we use object detection or style transfer for that part, the app would be like the screenshots at the beginning of this article.</p><h4>Receive user inputs</h4><p>Update the app.py as below.</p><pre>import streamlit as st<br>from streamlit_webrtc import webrtc_streamer<br>import av<br>import cv2<br><br>st.title(&quot;My first Streamlit app&quot;)<br>st.write(&quot;Hello, world&quot;)<br><br>threshold1 = st.slider(&quot;Threshold1&quot;, min_value=0, max_value=1000, step=1, value=100)<br>threshold2 = st.slider(&quot;Threshold2&quot;, min_value=0, max_value=1000, step=1, value=200)<br><br><br>def callback(frame):<br>    img = frame.to_ndarray(format=&quot;bgr24&quot;)<br><br>    img = cv2.cvtColor(cv2.Canny(img, threshold1, threshold2), cv2.COLOR_GRAY2BGR)<br><br>    return av.VideoFrame.from_ndarray(img, format=&quot;bgr24&quot;)<br><br><br>webrtc_streamer(key=&quot;example&quot;, video_frame_callback=callback)</pre><p>Then click the “START” button. You will find that there are 2 sliders in this example. You can modify the parameters of cv2.Canny() through the sliders, even during execution in real time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*BZbFekJtJtfG2MFNbZAZGA.gif" /></figure><p>With this update,</p><ul><li>We added threshold1 and threshold2 variables.</li><li>We added two slider components with st.slider() and assigned their values to these variables. st.slider() is a built-in component of Streamlit. Its official API reference is <a href="https://docs.streamlit.io/library/api-reference/widgets/st.slider">https://docs.streamlit.io/library/api-reference/widgets/st.slider</a>.</li><li>We then passed these variables to cv2.Canny() inside the callback as its parameters.</li></ul><p>Now we have interactive inputs to control the real-time video filter!</p><h4>Execution model of the callback and an important notice about it</h4><p>Unlike OpenCV, streamlit-webrtc requires callbacks to process image and audio frames. This callback-based design is one major difference between OpenCV GUI and streamlit-webrtc, and there are a few things you have to be aware of about it.</p><p>Please note that the callback is executed in a forked thread different from the main thread where the Streamlit app code runs. It makes some restrictions as below.</p><ul><li>The global keyword does not work as expected inside the callback.</li><li>Streamlit methods such as st.write() cannot be used inside the callback.</li><li>Communications between inside and outside the callback must be thread-safe.</li></ul><h3>Deploy the app to the cloud</h3><p>We are going to make the web app available to everyone by deploying it to the cloud.</p><h4>Configure WebRTC</h4><p>To deploy the app to the cloud, we have to add rtc_configuration parameter to the webrtc_streamer().</p><pre>webrtc_streamer(<br>    key=&quot;example&quot;,<br>    video_frame_callback=callback,<br>    rtc_configuration={  # Add this line<br>        &quot;iceServers&quot;: [{&quot;urls&quot;: [&quot;stun:stun.l.google.com:19302&quot;]}]<br>    }<br>)</pre><p>This configuration is necessary to establish the media streaming connection when the server is on a remote host.</p><p>streamlit_webrtc uses WebRTC for its video and audio streaming. It has to access a &quot;STUN server&quot; in the global network for the remote peers (precisely, peers over the NATs) to establish WebRTC connections. While we don&#39;t look at the details about STUN servers in this article, please google it with keywords such as STUN, TURN, or NAT traversal if interested.</p><p>We configured the code to use a free STUN server provided by Google in the example above. You can also use any other available STUN servers.</p><p>The value of the rtc_configuration argument will be passed to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection">RTCPeerConnection</a> constructor on the frontend.</p><h4>HTTPS</h4><p>We have to serve web apps on remote hosts via HTTPS to use webcams or microphones.</p><p>Not only the webrtc_streamer() component we used here but also any frontend apps that access the client-side webcams or microphones use <a href="https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia">MediaDevices.getUserMedia()</a> API. This API does not work in an &quot;insecure context.&quot;</p><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security">document</a> says</p><blockquote><em>A secure context is, in short, a page loaded using HTTPS or the </em><em>file:/// URL scheme, or a page loaded from </em><em>localhost.</em></blockquote><blockquote><a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security"><em>MediaDevices.getUserMedia() - Privacy and security</em></a></blockquote><p>As a result, we need HTTPS to serve web apps on remote hosts which access the client-side webcams or microphones.</p><h4>Streamlit Cloud</h4><p>I recommend <a href="https://streamlit.io/cloud">Streamlit Cloud</a> for Streamlit app hosting. You can deploy the apps from GitHub repositories with a few clicks, and it automatically serves the apps via HTTPS. And Streamlit Cloud seems to provide better runtime than Heroku free-tier, while Streamlit Cloud provides a large deployment capacity for free.</p><p>Please refer to <a href="https://docs.streamlit.io/streamlit-cloud">the official document</a> for its usage.</p><p>I actually deployed the app we have seen in this article on Streamlit Cloud: <a href="https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py">https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py</a> .</p><p>Its GitHub repository is <a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample">https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample</a> .</p><p>Note that requirements.txt has been added to install the necessary dependencies ( streamlit-webrtc and opencv-python-headless) in the Streamlit Cloud environment: <a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample/blob/main/requirements.txt">https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample/blob/main/requirements.txt</a></p><h3>Notice</h3><p>As written above, the video and audio streams sourced from the client devices are transmitted to and processed at the server.</p><p>So, this library is not scalable and depends on network connectivity. You may think of it mainly for prototyping or demo purpose.</p><p>You also have to consider hosting the apps in local networks if there are concerns about transmitting media to the remote cloud server.</p><h3>Examples</h3><p>This section is a copy of the sample list at <a href="https://github.com/whitphx/streamlit-webrtc">https://github.com/whitphx/streamlit-webrtc</a>.</p><h4>Showcase including following examples and more</h4><p><a href="https://github.com/whitphx/streamlit-webrtc-example">⚡️Repository</a>, <a href="https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py">🎈Online demo</a></p><ul><li>Object detection (This is the sample app a screenshot of which is at the beginning of this article)</li><li>OpenCV filter</li><li>Uni-directional video streaming</li><li>Audio processing</li></ul><p>You can try out this sample app using the following commands on your local env.</p><pre>$ pip install streamlit-webrtc opencv-python-headless matplotlib pydub<br>$ streamlit run https://raw.githubusercontent.com/whitphx/streamlit-webrtc-example/main/app.py</pre><h4>Real-time Speech-to-Text</h4><p><a href="https://github.com/whitphx/streamlit-stt-app">⚡️Repository</a>, <a href="https://share.streamlit.io/whitphx/streamlit-stt-app/main/app_deepspeech.py">🎈Online demo</a></p><p>It converts your voice into text in real time. This app is self-contained; it does not depend on any external API.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*YfONkyW8hE0xn-Mh8R_WFA.gif" /></figure><h4>Real-time video style transfer</h4><p><a href="https://github.com/whitphx/style-transfer-web-app">⚡️Repository</a>, <a href="https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py">🎈Online demo</a></p><p>It applies a wide variety of style transfer filters to real-time video streams.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*1Wuc0VtaduShJnPF.gif" /></figure><h4>Video chat</h4><p><a href="https://github.com/whitphx/streamlit-video-chat-example">⚡️Repository</a> (Online demo not available)</p><p>You can create video chat apps with ~100 lines of Python code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U4YKnPDvPSCR2hVXKKpf4A.jpeg" /></figure><h4>Tokyo 2020 Pictogram</h4><p><a href="https://github.com/whitphx/Tokyo2020-Pictogram-using-MediaPipe">⚡️Repository</a>: <a href="https://share.streamlit.io/whitphx/tokyo2020-pictogram-using-mediapipe/streamlit-app">🎈Online demo</a></p><p><a href="https://google.github.io/mediapipe/">MediaPipe</a> is used for pose estimation.</p><h3>Yuichiro on Twitter: &quot;Pictogram app on web browsers.After forking the original code which uses OpenCV, it takes only 2 hours to add the web UI supporting real time video streams with Streamlit and streamlit-webrtc.https://t.co/VRpB27enlI https://t.co/MQAWJVqkiM / Twitter&quot;</h3><p>Pictogram app on web browsers.After forking the original code which uses OpenCV, it takes only 2 hours to add the web UI supporting real time video streams with Streamlit and streamlit-webrtc.https://t.co/VRpB27enlI https://t.co/MQAWJVqkiM</p><h3>What about audio?</h3><p>You can deal with audio streams in a similar way as video. If you define a callback function and pass it to the audio_frame_callback argument, the callback will be executed with audio frames. In the case of audio, the input argument and the returned value of the callback are instances of <a href="https://pyav.org/docs/develop/api/audio.html#module-av.audio.frame">the </a><a href="https://pyav.org/docs/develop/api/audio.html#module-av.audio.frame">AudioFrame class</a>.</p><p>Please see the source code of <a href="https://github.com/whitphx/streamlit-webrtc/blob/c172483efd4566b18d3500e914285079117b5b35/pages/audio_filter.py">a sample app changing the audio gain</a> or the Speech-to-Text app in the examples above.</p><p><em>Originally published at </em><a href="https://www.whitphx.info/posts/20211231-streamlit-webrtc-video-app-tutorial/"><em>https://www.whitphx.info</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7c7bcd0bc5a8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/developing-web-based-real-time-video-audio-processing-apps-quickly-with-streamlit-7c7bcd0bc5a8">Developing Web-Based Real-Time Video/Audio Processing Apps Quickly with Streamlit</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Developing a streamlit-webrtc component for real-time video processing]]></title>
            <link>https://medium.com/data-science/developing-a-streamlit-webrtc-component-for-real-time-video-processing-5d4c07405c4d?source=rss-94247ffb9e0c------2</link>
            <guid isPermaLink="false">https://medium.com/p/5d4c07405c4d</guid>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[editors-pick]]></category>
            <category><![CDATA[streamlit]]></category>
            <category><![CDATA[hands-on-tutorials]]></category>
            <category><![CDATA[image-processing]]></category>
            <dc:creator><![CDATA[Yuichiro Tachibana (Tsuchiya)]]></dc:creator>
            <pubDate>Fri, 12 Feb 2021 17:48:52 GMT</pubDate>
            <atom:updated>2021-02-22T17:18:26.567Z</atom:updated>
            <content:encoded><![CDATA[<h4><a href="https://towardsdatascience.com/tagged/hands-on-tutorials">Hands-on Tutorials</a></h4><h4>Introducing the WebRTC component for real-time media streams</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AyvcTDJyNg-Wk2klSZuCLA.gif" /></figure><p>Real-time video processing is one of the most important applications when developing various computer vision or machine learning models. It’s useful because it allows users to quickly verify what their models can do with handy video input from their own devices, such as webcams or smartphones.</p><p>But it also presents a challenge to those of us using Streamlit, since Streamlit doesn’t natively support real-time video processing well yet through its own capabilities.</p><p>I created <a href="https://github.com/whitphx/streamlit-webrtc">streamlit-webrtc</a>, a component that enables Streamlit to handle real-time media streams over a network to solve this problem. In this in-depth tutorial, I’ll also briefly introduce you to WebRTC (check out <a href="https://dev.to/whitphx/python-webrtc-basics-with-aiortc-48id">my article here</a> for more in-depth info on WebRTC). If you want to jump right to playing with the component <a href="https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py">here is a sample</a> app.</p><p>Ready?</p><p>Let’s dive in.</p><p>(This tutorial requires Python &gt;= 3.6 and a webcam.)</p><h4>The problem with existing approaches</h4><p>Streamlit is actively used by many developers and researchers to prototype apps backed with computer vision and machine learning models, but it can’t yet natively support real-time video processing.</p><p>One existing approach to achieve real-time video processing with Streamlit is to use OpenCV to capture video streams. However, this only works when the Python process can access the video source — in other words, only when the camera is connected to the same host the app is running on.</p><p>Due to this limitation, there have always been problems with deploying the app to remote hosts and using it with video streams from local webcams. cv2.VideoCapture(0) consumes a video stream from the first (indexed as 0) locally connected device, and when the app is hosted on a remote server, the video source is a camera device connected to the <em>server </em>- not a local webcam.</p><h4>How WebRTC resolves this issue</h4><p>WebRTC (Web Real-Time Communication) enables web servers and clients, including web browsers, to send and receive video, audio, and arbitrary data streams over the network with low latency.</p><p>It is now supported by major browsers like Chrome, Firefox, and Safari, and its specs are open and standardized. Browser-based real-time video chat apps like Google Meet are common examples of WebRTC usage.</p><p>WebRTC extends Streamlit’s powerful capabilities to transmit video, audio, and arbitrary data streams between frontend and backend processes, like browser JavaScript and server-side Python.</p><h3>The WebRTC basics</h3><p>The following tutorial uses knowledge about WebRTC concepts such as “Signaling”, “Offer”, and “Answer”. The below figure provides a simple summary of how to establish a WebRTC connection.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-SL__RJkXVjok1wH.png" /></figure><ul><li>WebRTC has a preparation phase called “Signaling”, during which the peers exchange data called “offers” and “answers” in order to gather necessary information to establish the connection.</li><li>Developers choose an arbitrary method for Signaling, such as the HTTP req/res mechanism.</li></ul><p>If you want to know more about these concepts, read <a href="https://dev.to/whitphx/python-webrtc-basics-with-aiortc-48id">this article</a>.</p><p>Just as in the article linked above, <strong>this tutorial will use </strong><strong>aiortc, a Python library for WebRTC, and </strong><a href="https://github.com/aiortc/aiortc/tree/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server"><strong>an example from the </strong></a><a href="https://github.com/aiortc/aiortc/tree/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server"><strong>aiortc repository</strong></a><strong> as the basis for our sample project.</strong></p><h3>The basics of Streamlit’s execution model</h3><p>To read further, you should know about the development of Streamlit bi-directional custom components and about Streamlit’s execution model. You can learn about it <a href="https://docs.streamlit.io/en/stable/streamlit_components.html">here</a>.</p><p>Here is a short summary:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OE2UaH2TVlKlkNPS.png" /></figure><ul><li>Upon each execution, the Python script is executed from top to bottom.</li><li>Each execution of the Python script renders the frontend view, sending data from Python to JS as arguments to the component.</li><li>The frontend triggers the next execution via Streamlit.setComponentValue(), sending data from JS to Python as a component value.</li></ul><h3>Integrate aiortc into a Streamlit component</h3><p>In this section, to understand how to integrate a WebRTC implementation into a Streamlit custom component, we will create a minimal version of streamlit-webrtc called tiny-streamlit-webrtc, as a hands-on tutorial.</p><p>The source code of tiny-streamlit-webrtc is <a href="https://github.com/whitphx/tiny-streamlit-webrtc">hosted on GitHub</a>. Throughout this tutorial, we will refer to this repository and review each intermediate commit step-by-step to reach the final version.</p><p>It is recommended for you to clone the repository:</p><pre>$ git clone <a href="https://github.com/whitphx/tiny-streamlit-webrtc.git">https://github.com/whitphx/tiny-streamlit-webrtc.git</a><br>$ cd tiny-streamlit-webrtc</pre><p>With the below command, you can check out the specific revision referenced in each section in order to see the entire codebase and to actually try running it.</p><pre>$ git checkout &lt;revision&gt;</pre><h4>Install dependencies</h4><p>Install the necessary packages. Note that this tutorial does not work with the latest version of aiortc ( 1.1.1) and 1.0.0 must be used.</p><pre>$ pip install streamlit opencv-python<br>$ pip install aiortc==1.0.0</pre><h4>Setting up the project</h4><p>As usual, we start with the <a href="https://github.com/streamlit/component-template/tree/4b90f5277379a548792af51506254aee31854316/template">official template of a bi-directional component</a>. The reference <a href="https://github.com/whitphx/tiny-streamlit-webrtc">tiny-streamlit-webrtc implementation</a> is based on the revision 4b90f52.</p><p>After copying the template files, complete the rest of the setup, including the steps below.</p><ul><li>Rename “my_component&quot; to &quot;tiny_streamlit_webrtc&quot;.</li><li>Run npm install in tiny_streamlit_webrtc/frontend.</li><li>Remove the existent code, comments, and docstrings.</li><li>Add necessary files such as .gitignore</li></ul><p>Check out <a href="https://github.com/whitphx/tiny-streamlit-webrtc/compare/13660f3..f6daf28">what this section does</a>, with code version <a href="https://github.com/whitphx/tiny-streamlit-webrtc/tree/f6daf280c650c04ae45f114fff4a4d0fd39a14c1">f6daf28</a>.</p><h4>Rolling out the first frontend implementation</h4><p>Let’s start writing code.</p><p>First, we will simply copy and paste some lines of code from index.html and client.js in <a href="https://github.com/aiortc/aiortc/tree/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server">the </a><a href="https://github.com/aiortc/aiortc/tree/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server">aiortc example</a> into our React component, but with some fixes.</p><p><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/e3f70e44bbd17d383abfdbef2f2d9e961d1a47e6">e3f70e4</a>is the actual edit, and you can try this version by checking out the commit, as explained above.</p><pre>$ git checkout e3f70e4</pre><p>The view contains only a &lt;video /&gt; element with autoPlay and playsInline props, as it is in the original index.html, and a button element to start the WebRTC session. The start button&#39;s onClick handler is bound to the start() method, which is copied from client.js and slightly modified to remove some lines unnecessary for this tutorial and adjust to the React class-based component style. We will do the same for negotiate() and createPeerConnection().</p><p>Let’s run this component in the usual manner for Streamlit custom component development.</p><pre>$ cd tiny_streamlit_webrtc/frontend/<br>$ npm start</pre><pre>$ streamlit run tiny_streamlit_webrtc/__init__.py</pre><p>After opening the app with a web browser, open the developer tools, and click the “Start” button. You can see the offer is generated and printed in the console as below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lSsyyRqcq53i09Df.png" /></figure><p>This is printed via <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/e3f70e44bbd17d383abfdbef2f2d9e961d1a47e6#diff-c0bb5335a5a993d716414831b4151b2a7070e4533adb552831b5f22a4b32da1cR83">this line</a>. Please follow the steps leading up to it. This code is equivalent to <a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/client.js#L70">the code in the original example</a> <em>before</em> sending the offer to the Python server. Yes, this case is different from the original example. How can we send the offer to the Python process?</p><p>(You also see your webcam become active since <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/e3f70e44bbd17d383abfdbef2f2d9e961d1a47e6#diff-c0bb5335a5a993d716414831b4151b2a7070e4533adb552831b5f22a4b32da1cR95">navigator.mediaDevices.getUserMedia()</a> requests its use.)</p><h4>Send offer from JS to Python</h4><p>streamlit-webrtc makes use of Streamlit.setComponentValue() for this purpose. We will learn about it in this section.</p><p><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/7b7dd2d2f9289b7f6697a3ab915fd8dc6438afd9">7b7dd2d</a>is the next update. Use git checkout 7b7dd2d to check out it.</p><p>With this change, the offer is sent from the frontend to the server as a component value.</p><pre>const offerJson = offer.toJSON()<br>Streamlit.setComponentValue({<br>  offerJson,<br>})</pre><p>The offer can be read on the server-side as below.</p><pre>component_value = _component_func(key=key, default=None)<br>if component_value:<br>    offer_json = component_value[&quot;offerJson&quot;]</pre><p>Let’s run this version and confirm the offer is displayed after clicking the “Start” button, which means the offer is received by the Python process and shown with st.write()<a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/7b7dd2d2f9289b7f6697a3ab915fd8dc6438afd9#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR24"> here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mqaQK4GoRF98mDdx.png" /></figure><h4>Server-side implementation with asyncio</h4><p>Now the offer is received on the server-side, so let’s implement the code to process it. Just as we did with the frontend, let’s copy and paste from the example server.py to our streamlit_webrtc/__init__.py, like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/92416e63b6d09ee77f6b5739af73a888d15fe96b">this</a>, which is copied from <a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/server.py#L102-L167">offer() coroutine in the example </a><a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/server.py#L102-L167">server.py</a>.</p><p>Note that a video transformer is temporarily omitted from the track event listener <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/92416e63b6d09ee77f6b5739af73a888d15fe96b#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR37">like this</a> to focus on the WebRTC part for now. It now just passes through the input track to the output.</p><p>However, as you can see, this code contains async and await and does not work in a function. So, we have to wrap this part in a coroutine like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/a6f7cc050b5fd07f49800bf264ec7fc34d70bdbb">this</a>.</p><p>Please run <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/a6f7cc050b5fd07f49800bf264ec7fc34d70bdbb">this version: </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/a6f7cc050b5fd07f49800bf264ec7fc34d70bdbb">a6f7cc0</a> and confirm the answer is displayed following the offer from <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/a6f7cc050b5fd07f49800bf264ec7fc34d70bdbb#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR55">here</a>. That means the server-side pc object has processed the offer and generated the answer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kYIINLsTAKFPRQMz.png" /></figure><p>What we have to do next is send it back to the frontend.</p><h4>Send back the answer from Python to JS</h4><p>To do this, streamlit-webrtc simply relies on Streamlit&#39;s data sending mechanism from Python to JavaScript as below.</p><pre>_component_func(key=key, answer=answer)</pre><p>However, one problem arises. We’ve already called component_value = _component_func(...) and obtained the offer from it. After that, we generated the answer. So, how can we set the argument to the already called _component_func() again?</p><p>Simply calling the second _component_func() as below does not work, because in the Streamlit app, different _component_func() calls are recognized as different instances of the component.</p><pre>component_value = _component_func()<br>offer = component_value[&quot;offer&quot;]<br>answer = generate_answer(offer)  # Pseudo code<br>_component_func(answer=answer)  # This does not work!</pre><p>To resolve this problem, we have to introduce a hack: SessionState and st.experimental_rerun(). With these tools, we can rerun the script to call a _component_func() in the same line again and hold a variable over the runs to feed it to the _component_func() in the second and later executions.</p><p>SessionState has been discussed in <a href="https://discuss.streamlit.io/t/is-there-any-working-example-for-session-state-for-streamlit-version-0-63-1/4551">this forum topic</a> and the source is available on <a href="https://gist.github.com/tvst/036da038ab3e999a64497f42de966a92">this page in Gist</a>.</p><p>st.experimental_rerun() seems, as its name implies, to be an experimental API and not documented yet. It has been discussed in <a href="https://github.com/streamlit/streamlit/issues/653">this GitHub issue</a> and can now be used.</p><p>Please see <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7e">this version of the server-side code</a>, where SessionState and st.experimental_rerun() are used to feed the generated answer to the component.</p><p>This illustrates how it works.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kILBO2dIiXeX_z8k.png" /></figure><p>Another important thing here is that the key argument is no longer optional but must be explicitly provided like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR83">this</a>. As the answer is fed as an argument to _component_func() and its value changes over the runs, key is necessary as a stable identifier of the component instance.</p><p>If key is None, Streamlit identifies the component instance based on arguments other than key, so Streamlit cannot trace the identity of the component instance over the runs as the answer changes.</p><p>Note that <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR64">this if-clause</a> is added to invoke st.experimental_rerun() only the first time the server-side process gets the offer from the frontend. This may also be achieved by resetting the component value on the frontend once the offer is passed to Python.</p><p>With <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758">this version: </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758">aa2ab49</a>, you can see the answer is provided as a field of the args prop like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/aa2ab49606060e4a038f392500aecbe95c4ec758#diff-c0bb5335a5a993d716414831b4151b2a7070e4533adb552831b5f22a4b32da1c">this</a> on the frontend. Let&#39;s confirm it with the browser&#39;s devtools.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rxXxegbTTarM2FKD.png" /></figure><h4>Implement processAnswer()</h4><p>Now we have the answer on the frontend. Let’s implement the rest of the frontend code like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/7fbf0eb5a72de4ea84b21708df80a396fa3222ff">this</a>.</p><p>This code is copied from the <a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/client.js#L95-L99">part following receiving the answer in the example </a><a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/client.js#L95-L99">client.js</a> and fixed to adjust to ours.</p><h4>Introduce a thread running over script executions</h4><p>It seems we have done all things we have to do, but no video appears when you click the “Start” button with <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/7fbf0eb5a72de4ea84b21708df80a396fa3222ff">this version: </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/7fbf0eb5a72de4ea84b21708df80a396fa3222ff">7fbf0eb</a>.</p><p>The problem resides on the server-side. The server-side WebRTC code from aiortc runs on an event loop, which is implicitly started with asyncio.run() <a href="https://github.com/whitphx/tiny-streamlit-webrtc/blob/7fbf0eb5a72de4ea84b21708df80a396fa3222ff/tiny_streamlit_webrtc/__init__.py#L67">here</a> now. An event loop is created on which aiortc functions rely throughout one Streamlit script execution. But this event loop will be trashed in the next script execution and the aiortc can no longer keep working.</p><p>To resolve this problem, we will fork a thread and create an event loop inside it to run aiortc functions. And the thread object is stored in the SessionState to be maintained over the multiple Streamlit script executions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KJg2DDPMN6Vln8wL.png" /></figure><p>See <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8">this version of the code: </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8">093f81b</a>. <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR45-R67">This </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR45-R67">webrtc_worker() function</a> is forked as a thread <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR93">here</a>. Inside this thread, <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR48">a new event loop is created</a> and the process_offer() coroutine is running on it - which was invoked by asyncio.run() in the previous revisions of this code. With this change, <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR92">queue.Queue is introduced</a> to <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/093f81be648ad66082e15448b90b74e24e5897b8#diff-6abc149a63c4cc57ae904cb1f4bad9f0d063f70011a4cb9266fb4411ec839a7eR97">get the answer object in the main thread</a>, which is now generated in the forked thread.</p><p>There is one drawback of forking a thread — the streamlit run command does not stop when you hit Ctrl+c. This is because the forked thread remains even after the main thread is terminated.</p><p>To forcefully terminate the process, send it SIGKILL as below.</p><pre>$ ps aux | grep python | grep streamlit # Find the process ID whitphx 19118 11.2 0.6 4759304 99928 s003 S+ 5:27PM 0:02.06 /path/to/venv/bin/python3.8 /path/to/venv/bin/streamlit run tiny_streamlit_webrtc/__init__.py<br>$ kill -9 19118 # Send SIGKILL to the process specified with the ID</pre><p>To fix it, the daemon option of the forked thread is set to True like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/fc48060224bd69e85528a4b0859107a70dfbe0bf">this</a>. With this flag, the script stops correctly when necessary.</p><blockquote><em>A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.<br></em><a href="https://docs.python.org/3/library/threading.html#thread-objects"><em>“Thread Objects”</em></a><em> (Python.org)</em></blockquote><h4>Component height adjustment</h4><p>Let’s try out the <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/fc48060224bd69e85528a4b0859107a70dfbe0bf">current version: </a><a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/fc48060224bd69e85528a4b0859107a70dfbe0bf">fc48060</a>. Now, WebRTC works and the video appears with this component! However, the displayed video is cropped and the lower part of it is hidden like below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sVeFNfNuvw74B_HU.png" /></figure><p>To fix it, we have to call Streamlit.setFrameHeight() when the size of &lt;video /&gt; element changes. Although it is automatically called when the props are updated, the element resize is not associated with props updates but with starting video streaming.</p><p>Now attach onCanPlay event handler on the &lt;video /&gt; element and call Streamlit.setFrameHeight() from it like <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/1a57a9755e0325ed3839bb259a7655bfe94b679e#diff-c0bb5335a5a993d716414831b4151b2a7070e4533adb552831b5f22a4b32da1cR39">this</a>. (While using <a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver">ResizeObserver</a> may be the right way to observe DOM element resizes, we use the onCanPlay event here as a substitute, for simplicity&#39;s sake.)</p><p>Cool! Now it works correctly. 🎉 <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/1a57a9755e0325ed3839bb259a7655bfe94b679e">1a57a97</a> is this version.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L0ISFkIZTPRPQBi-.png" /></figure><p>Now all the core parts for WebRTC are complete. We’ll implement the rest in the following sections.</p><h4>Implementing your own video filter</h4><p>First, let’s try to implement some video filters. <a href="https://github.com/whitphx/tiny-streamlit-webrtc/commit/3ba703d1604c9edecedcf9bb3bed32706ada023a">3ba703d</a> is an example with a simple edge extractor, copied from the <a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/server.py#L66-L75">sample code of </a><a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/server.py#L66-L75">aiortc</a>.</p><h4>Implement a stop button</h4><p>Refer to <a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/client.js#L184-L210">the </a><a href="https://github.com/aiortc/aiortc/blob/2362e6d1f0c730a0f8c387bbea76546775ad2fe8/examples/server/client.js#L184-L210">aiortc example</a> to create a stop button to gracefully stop the stream. is the current version.</p><h3>The execution model of streamlit-webrtc</h3><p>We have followed the steps to develop a minimal Streamlit component utilizing WebRTC to stream video.</p><p>As we’ve seen in this component, we chose a design in which the computer vision code is running in a callback in the forked thread, triggered by new frame arrivals from the input stream, independent of Streamlit’s script execution timings. It looks a little bit weird the first time you see it, but it’s necessary and natural when dealing with real-time streams.</p><p>Let’s see it from a more abstract view. When processing frames coming from real-time streams, the streams are additional event sources other than user interactions through the frontend view. In normal Streamlit apps, all the events triggering Python script executions are only sourced from the frontend and they are nicely encapsulated by Streamlit.</p><p>With its execution model, then, developers can write the apps in a clean world where there are no callbacks and no (or little) side effects. In turn, if we want to handle the streams with good performance, we have to explicitly handle the events sourced from the streams like frame generations, which breaks the elegant encapsulation, causing callbacks and events to appear in the script.</p><h3>What tiny-streamlit-webrtc lacks</h3><p>Though we’ve created a small subset of <a href="https://github.com/whitphx/streamlit-webrtc">streamlit-webrtc</a>, tiny-streamlit-webrtc, it still lacks many important features streamlit-webrtc has. Here we will review some of them.</p><h4>Parameters input from Streamlit components</h4><p>One of the biggest benefits of using Streamlit is interactive controls such as sliders and radio buttons. With computer vision and machine learning models, these controls are very useful to change the parameters during execution.</p><p>Because the computer vision code is running in the forked thread with this component, we have to pass the values obtained from Streamlit widgets to the CV code over the threads. But it is not difficult, like <a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/app.py#L184">here in the </a><a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/app.py#L184">streamlit-webrtc sample</a>.</p><p>With tiny-streamlit-webrtc, you can do this by adding a public property to VideoTransformTrack and read and write it from each thread, just like the sample code linked above. Please try it if you are interested, and be careful about thread safety when you pass complex values.</p><h4>Frame drops</h4><p>We’ve used edge extraction as an example in the tutorial. However, if you replace it with more computationally expensive filters like deep neural networks, you will see the displayed video slows down. You can test it simply by putting time.sleep(1) in VideoTransformTrack.recv().</p><p>This is because VideoTransformTrack.recv() processes all the input frames one by one - if it delays, generating the output frames is also delayed.</p><p>To solve this problem, VideoTransformTrack.recv() has to drop some input frames and pick the latest one each time it runs. In streamlit-webrtc, it&#39;s done <a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/streamlit_webrtc/transform.py#L97-L98">here</a> when <a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/streamlit_webrtc/__init__.py#L98">async_transform </a>option is set as True.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Zl8bLRYcQyiXZAle.png" /></figure><h4>Extendability</h4><p>In tiny-streamlit-webrtc, the video transformation is hard-coded inside VideoTransformTrack.recv(), but of course, this is bad design as a library. To be reusable, it should expose an injectable interface through which developers can implement arbitrary kinds of video transformation, encapsulating details such as VideoTransformTrack.recv() and WebRTC-related code.</p><p>With streamlit-webrtc, developers can implement their own video transformations by creating a class extending VideoTransformerBase class like <a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/app.py#L233">this</a> and <a href="https://github.com/whitphx/streamlit-webrtc/blob/f03d3150adfa27c44bb7f2d22d495351090d9341/app.py#L129">this</a>.</p><h3>Key takeaways</h3><p>Streamlit is a nifty framework with a useful library, but it doesn’t handle real-time video processing well on its own.</p><p>WebRTC makes Streamlit even more awesome by enabling server-side processes and clients to send and receive data streams over the network with low latency.</p><p>Have an amazing project in mind to use WebRTC for? Share it with us in the comments or <a href="https://discuss.streamlit.io/u/whitphx">message me</a>.</p><h3>Credits</h3><p>Reviewed by Yu Tachibana (<a href="https://twitter.com/z_reactor">@z_reactor</a>)</p><p><em>Originally published at </em><a href="https://blog.streamlit.io/how-to-build-the-streamlit-webrtc-component/"><em>https://blog.streamlit.io</em></a><em> on February 12, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5d4c07405c4d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/developing-a-streamlit-webrtc-component-for-real-time-video-processing-5d4c07405c4d">Developing a streamlit-webrtc component for real-time video processing</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>