Accessing Temporary Files On Streamlit Sharing

Hello,

I deployed a computer vision app that works with OpenCV and Mediapipe and (also) takes video inputs. My idea was to process the input video (first 10 seconds only); write a new output video to a temporary folder, display the output video using st.video() and clean up the temporary folder. The code looks like this:

if type in ["video", "webcam"]:
    # some code
    codec = cv.VideoWriter_fourcc(*"avc1")
    max_frames = 10 * vid_fps

    temp_dir = tempfile.mkdtemp(suffix=None, prefix="SMW_", dir=None)
    if not os.path.lexists(temp_dir):
        os.mkdir(temp_dir)
    out_vid_file = os.path.join(temp_dir, "vid_output.mp4")
    out_vid = cv.VideoWriter(out_vid_file, codec, vid_fps, (vid_w, vid_h))

    if start_clicked:
        placeholders[1].info("Processing video input...")
        progress_bar = st.progress(0)

        while media.isOpened():
            # some opencv code
            out_vid.write(cv.cvtColor(img, cv.COLOR_RGB2BGR))
            # some opencv code

            if out_vid_frame_count == max_frames:
                media.release()
                out_vid.release()
                break

        st.video(out_vid_file)
        shutil.rmtree(temp_dir, ignore_errors=True)

This works okay on my PC, but when on Streamlit sharing, I get a FileNotFoundError at the st.video(out_vid_file) line. I have checked, the temporary folder and output video are created but once the code exits the loop, they get cleaned up before the video can be displayed.

I have also tried to create a temporary folder in the same folder as the python file using temp_dir = os.path.join(os.path.dirname(__file__), "output_vid") but it’s the same issue.

I will appreciate any information on this issue and any workarounds available. Thank you.

Hello,

I am trying to deploy a similar app at Streamlit Share and I am facing the same problems you described in your topic. I am also truing to save video as temporary file and then read it and display with st.video().

Had you succeed with your application? Did you find a way to make this pipeline workable or maybe you’ve found some other approach? Thanks!

Hello,

I disabled that feature on my app. I planned on fixing it later but unfortunately I moved on to other projects.

All the best. Cheers!

Hey again.

Just letting you know that I’ve found a solution to my issue. I’ve succeed with uploading a video to Streamlit Cloud, processing it with OpenCV functions (and with DL models as next steps), rendering it to a video file using h264 codec and displaying it at my Streamlit web page.

I’ve managed to do that using the tempfile library to create a temp file where I put a rendered video. Use it and the issue with FileNotFoundError should be gone. But this was not the main problem.

The main problem was to render a video with h264 codec to display it in Streamlit’s html5 web player. The point is that OpenCV doesn’t support h264 from scratch, you should either manually build it (I can’t even imagine how to do that for Streamlit Cloud) or just put the openh264-1.8.0-win64.dll file to your project folder (this is what I did to make my script work locally, I assume that it was your approach as well, otherwise cv.VideoWriter_fourcc(*“avc1”) just won’t work).

The openh264-1.8.0-win64.dll file obviously compiled for Windows, so such approach of course doesn’t work at Streamlit Cloud. Maybe it’s possible to find a compiled h264 codec for Streamlit OS, but first I am not even sure what version of Linux they use and second I don’t know if OpenCV will be able to implicitly attach this codec in Streamlit Cloud file system. So I gave up this idea anyway.

Instead I used the moviepy library to render a video in h264. It’s a wrapper over ffmpeg and It provides pretty straight-forward and easy functions to cut, edit and render video clips. To save video at Streamlit Cloud I need to save all numpy video frames to memory and then render it to drive, it’s not a very optimized approach. But it works for now, maybe I will improve it in future. Here goes the code of my app, something like that should work for you.

import streamlit as st
import cv2
import tempfile
import moviepy.editor as mpy

video_data = st.file_uploader("Upload file", ['mp4','mov', 'avi'])

if video_data:
    # save uploaded video to disc
    temp_file_1 = tempfile.NamedTemporaryFile(delete=False,suffix='.mp4')
    temp_file_1.write(video_data.getbuffer())
    # read it with cv2.VideoCapture(), so now we can process it with OpenCV functions
    cap = cv2.VideoCapture(temp_file_1.name)
    # grab some parameters of video to use them for writing a new, processed video
    frame_fps = int(cap.get(cv2.CAP_PROP_FPS))
    # specify a writer to write a processed video to a disk frame by frame
    temp_file_2 = tempfile.NamedTemporaryFile(delete=False,suffix='.mp4')
    # loop though a video, process each frame and save it to a list
    video_row=[]
    while True:
        ret, frame = cap.read()
        # if frame is read correctly ret is True
        if not ret:
            st.write("Video is ready! Now you can play it")
            break
        # some video processing here
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # write a processed frame to the list
        gray = cv2.merge((gray,gray,gray))
        video_row.append(gray)

    ## Close video file
    cap.release()
    # create moviepy object from list with numpy frames and save it to a disk
    clip = mpy.ImageSequenceClip(video_row, fps=25)
    clip.write_videofile(temp_file_2.name)
    # when video is fully saved to disk, open it as BytesIO and play with st.video()
    # result_video = open(temp_file_result, "rb")

    st.video(temp_file_2.name)
    result_video = open(temp_file_2.name, "rb")
    st.download_button(label="Download video file", data=result_video,file_name='video_clip.mp4')

Just in case if you still want to render your video precisely with OpenCV cv.VideoWriter method you will need to find a way to deploy it to Streamlit Cloud with h264. You may render your video with ‘mp4v’ codec instead, in that case you won’t be able to display it with st.video() method. But you still will be able to download this rendered video file with st.download_button.

I hope that helps with your issue. Good luck.

1 Like

Hello,

Thanks for the update. I looked over my code to jog my memory about what really went wrong.

Looking at the issue I opened, my problem was accessing the temp_dir created. I also wrote my processed numpy video frames to a file within temp_dir. However, after creating the video file, the file got “lost” before it could be displayed.

I’m glad you got the tempfile library working with videos on Streamlit. I used it successfully for all my images but somehow, it had issues with videos. Maybe the Streamlit devs made some changes between then and now (or I was just doing something wrong then).

Finally, I also have the line in my code (the comment speaks for itself lol):

codec = cv.VideoWriter_fourcc(*"avc1")  # *"mp4v" doesn't play with streamlit

I don’t recall what I did to make it work on Windows (or if I did anything at all). I certainly didn’t build OpenCV manually and I don’t have openh264-1.8.0-win64.dll in my project folder. However, if I recall correctly, the solution for displaying the videos on Streamlit Cloud was to add a packages.txt file (similar to requirements.txt) in my project folder/repository with the following contents:

freeglut3-dev
libgtk2.0-dev
libgl1-mesa-glx
tesseract-ocr
libtesseract-dev
libtesseract4
tesseract-ocr-all

You can check out the GitHub repository if you want to take a look at the entire project.

I really apologise for not being able to provide you with definite information on this issue. As I mentioned earlier, it has been a while and I’m worked on a bunch of stuff since then. Still, I would love to check out your app once it is deployed. Kindly share a link with me.

Cheers. :v:

2 Likes