why is psychopy.visual.MovieStim3 so slow in my use case? - python

Currently I would like to continually play movies in a loop from different filepaths using python 3.6, psychopy 1.90.2. The filepaths are listed in a csv file and each filepath has common ancestors but has a different parent directory and filename. e.g. '/media/michael/shared_network_drive/dataset/training/jumping/man_jumps_through_hoop3342.mp4' and '/media/michael/shared_network_drive/dataset/training/shouting/h555502.mp4'.
Currently there is a very large delay when creating the visual.MovieStim3 object which results in a large delay before every video. Here is the code so far:
def play_videos(csv_file, vid_location='/media/michael/shared_network_drive/dataset/training/'):
# Open a window
win = visual.Window([400,400])
#open csv file and cycle through each video
for vid, label, val1, val2 in csv.reader(open(csv_file, 'r')):
glob_vid_path = vid_location + vid
# Define a MovieStim3 object
mov = visual.MovieStim3(win, glob_vid_path, flipVert=False, flipHoriz=False)
# Loop through each frame of the video
while mov.status != visual.FINISHED:
mov.draw()
win.flip()
win.close()
Why is the delay so long and how can I overcome this?

For those with similar problems; the delay is caused by the location of the videos in a shared drive. Placing the videos on the home drive, or even an external hard-drive solved the problem.

Related

Is there a way to speed up my python video creation script?

So I wrote a script that creates a video out of a series of images. I have the images stored in a folder called "TEMP". The part of the script that takes a very long time is the following:
from moviepy.editor import *
def createVideoFromImages(genre, audio):
#export name
timeStamp = str(time.time()).split(".")[0]
exportFolderName = f"./finished/{genre}{timeStamp}"
exportFileName = f"{exportFolderName}/video.mp4"
images = os.listdir("TEMP")
clips = [ImageClip(f"./TEMP/{m}").set_duration(10).crossfadein(1.0) for m in images]
concat_clip = concatenate_videoclips(clips, method="compose")
audio_duration = audio.duration
videoclip = concat_clip.set_duration(audio_duration)
exportClip = videoclip.set_audio(audio)
#create folder and save video there
os.mkdir(exportFolderName)
exportClip.write_videofile(exportFileName, fps=60, preset="ultrafast")
return exportFolderName
I tried a couple of things, like changing the concatenation of videoclips to method="chain" but that broke and I got a glitchy video, where only the first image was properly showing.
I also tried to add the preset="ultrafast" as I found somewhere online, but I find it to slow things down rather than speed things up.
I suspect the script to run this slow(it takes 8-9 hours for an ~300 second video) because it takes almost all the RAM of my computer.
Is there a way to speed up this script, preferrably with a minimal sacrifice of quality?

Why is the last frame always excluded, when converting images into a video?

I have a folder with 225 pictures of maps. So, I compiled it to an mp4 file using imageio. Whether it's compiling 10 maps, 150, or all 225, the last picture is always not included in the video.
import os
from natsort import humansorted
import imageio
os.chdir(r'folder/path/')
filenames = humansorted((fn for fn in os.listdir('.') if fn.endswith('.png')))
with imageio.get_writer('earthquake_video.mp4', mode='I', fps=2) as writer:
for filename in filenames:
image = imageio.imread(filename)
writer.append_data(image)
writer.close()
For me, your code works fine for 10, 150, or even 225 images – as long as I open the resulting video in Windows Media Player. If I open the video in VLC media player, I get distorted playback, not only skipping the last frame. (I have numbers counting from 0 to 224, so every mistaken frame is noticed.) So, if you use VLC media player, your problem most likely is the one discussed in this StackOverflow Q&A.
On the imageio GitHub issue tracker, there's also this issue, linking to this other StackOverflow question, which seems to be same issue as you have. But, still, I think it's the afore-mentioned issue with the VLC media player.
Unfortunately, I couldn't get the workaround from the first linked Q&A working using the output_params from imageio, i.e. setting -framerate or -r. So, my workaround here would be to set up a desired fps (here: 2), and a fps for the actual playback (in VLC media player), e.g. 30, and then simply add as many identical frames as needed to fake the desired fps, i.e. 30 // 2 = 15 here.
Here's some code:
import os
import imageio
os.chdir(r'folder/path')
filenames = [fn for fn in os.listdir('.') if fn.endswith('.png')]
fps = 2 # Desired, "real" frames per second
fps_vlc = 30 # Frames per second needed for proper playback in VLC
with imageio.get_writer('earthquake_video.mp4', fps=fps_vlc) as writer:
for filename in filenames:
image = imageio.imread(filename)
for i in range(fps_vlc // fps):
writer.append_data(image)
writer.close()
The resulting video "looks" the same as before (in Windows Media player), but now, it's also properly played in VLC media player.
Even, if that's not YOUR actual problem, I guess this information will help other coming across your question, but actually suffering from the stated issue with the VLC media player.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1.1
imageio: 2.9.0
----------------------------------------

Piped FFMPEG won't write frames correctly

I am using Python's Image module to load JPEGs and modify them. After I have a modified image, I want to load that image in to a video, using more modified images as frames in my video.
I have 3 programs written to do this:
ImEdit (My image editing module that I wrote)
VideoWriter (writes to an mp4 file using FFMPEG) and
VideoMaker (The file I'm using to do everything)
My VideoWriter looks like this...
import subprocess as sp
import os
import Image
FFMPEG_BIN = "ffmpeg"
class VideoWriter():
def __init__(self,xsize=480,ysize=360,FPS=29,
outDir=None,outFile=None):
if outDir is None:
print("No specified output directory. Using default.")
outDir = "./VideoOut"
if outFile is None:
print("No specified output file. Setting temporary.")
outFile = "temp.mp4"
if (outDir and outFile) is True:
if os.path.exists(outDir+outFile):
print("File path",outDir+outFile, "already exists:",
"change output filename or",
"overwriting will occur.")
self.outDir = outDir
self.outFile = outFile
self.xsize,self.ysize,self.FPS = xsize,ysize,FPS
self.buildWriter()
def setOutFile(self,fileName):
self.outFile = filename
def setOutDir(self,dirName):
self.outDir = dirName
def buildWriter(self):
commandWriter = [FFMPEG_BIN,
'-y',
'-f', 'rawvideo',
'-vcodec','mjpeg',
'-s', '480x360',#.format(480,
'-i', '-',
'-an', #No audio
'-r', str(29),
'./{}//{}'.format(self.outDir,self.outFile)]
self.pW = sp.Popen(commandWriter,
stdin = sp.PIPE)
def writeFrame(self,ImEditObj):
stringData = ImEditObj.getIm().tostring()
im = Image.fromstring("RGB",(309,424),stringData)
im.save(self.pW.stdin, "JPEG")
self.pW.stdin.flush()
def finish(self):
self.pW.communicate()
self.pW.stdin.close()
ImEditObj.getIm() returns an instance of a Python Image object
This code works to the extent that I can load one frame in to the video and no matter how many more calls to writeFrame that I do, the video only every ends up being one frame long. I have other code that works as far as making a video out of single frames and that code is nearly identical to this code. I don't know what difference there is though that makes this code not work as intended where the other code does work.
My question is...
How can I modify my VideoWriter class so that I can pass in an instance of an Python's Image object and write that frame to an output file? I also would like to be able to write more than one frame to the video.
I've spent 5 hours or more trying to debug this, having not found anything helpful on the internet, so if I missed any StackOverflow questions that would point me in the right direction, those would be appreciated...
EDIT:
After a bit more debugging, the issue may have been that I was trying to write to a file that already existed, however, this doesn't make much sense with the -y flag in my commandWriter. the -y flag should overwrite any file that already exists. Any thoughts on that?
I suggest that you follow the OpenCV tutorial in writing videos. This is a very common way of writing video files from Python, so you should find many answers on the internet, if you can't get certain things to work.
Note that the VideoWriter will discard (and won't write) any frames that are not in the exact same pixel size that you give it on initialization.

Switching BG using Python

I'm writing a simple program that changes my background when I run it.
I'm trying to make it so that when I run it, it changes the background then when I run it again it goes to the next image in the list and so on.
But I can't figure out how to make it so that each time I run it, it picks the next image in the list and assigns it to the image variable.
Here is my code:
Img_list = ['C:\BG\mod_bg.bmp','C:\BG\BGMATRIX.jpg'] #Image List
pathToImg = #image name here
SPI_SETDESKWALLPAPER = 20
ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, pathToImg,
0)
so the first time I run it I want it to use C:\BG\mod_bg.bmpthen close then the next time I open it I want it to use C:\BG\BGMATRIX.jpg and close
please help.
To do that you will have to save the index in Img_list of the current picture somewhere. When your program starts it reads the index value, changes it and writes it back.
Places where you could store it are e.g.
a (hidden) file
the windows registry

Python images display

How can I create a python script that runs through the images (1.jpeg-n.jpeg) in a directory on a mac and displays them in a browser OR via another python program?
Do I import a file to python and than display in browser?
Do I extract the file names 1,2,3,4,5 and add that to a list, which I give to another function that calls a browser and displays?
Any help would be great.
Thanks!
Using Tkinter and PIL for this purpose is pretty trivial. Add muskies example to the information from this thread that contains this example:
# use a Tkinter label as a panel/frame with a background image
# note that Tkinter only reads gif and ppm images
# use the Python Image Library (PIL) for other image formats
# free from [url]http://www.pythonware.com/products/pil/index.htm[/url]
# give Tkinter a namespace to avoid conflicts with PIL
# (they both have a class named Image)
import Tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
root.title('background image')
# pick an image file you have .bmp .jpg .gif. .png
# load the file and covert it to a Tkinter image object
imageFile = "Flowers.jpg"
image1 = ImageTk.PhotoImage(Image.open(imageFile))
# get the image size
w = image1.width()
h = image1.height()
# position coordinates of root 'upper left corner'
x = 0
y = 0
# make the root window the size of the image
root.geometry("%dx%d+%d+%d" % (w, h, x, y))
# root has no image argument, so use a label as a panel
panel1 = tk.Label(root, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
# put a button on the image panel to test it
button2 = tk.Button(panel1, text='button2')
button2.pack(side='top')
# save the panel's image from 'garbage collection'
panel1.image = image1
# start the event loop
root.mainloop()
Of course if you're more familiar with another GUI, go ahead and adapt the example, it shouldn't take much.
You first have to find all image filenames. You can use os.listdir(...) to get all files in a certain directory, or glob.glob(...) to find all files matching a certain pattern.
Showing the images is the second and more challenging part of this. The first option is to open the images in an external program, this can be a web browser. On (most) platforms a command firefox 1.jpeg will open the image 1.jpeg in the Firefox browser. You can use the subprocess module to execute such commands. If you want to show them using a nice GUI, you have to create a GUI using some framework and use this. But if you are a beginner this might be a little bit too difficult for you.
For example:
import glob
import subprocess
files = glob.glob('dir/*.jpeg')
for file in files:
subprocess.call(['firefox', file])
muksie's answer already contains very useful advice. If don't want to write the HTML file yourself or want something a little fancier you could use a small script I wrote for the MDP library. This basically allows you to just do:
import slideshow
slideshow.show_image_slideshow(filenames, image_size=(80,60))
This will create an HTML slideshow and open it in your browser. You can grab just the needed files here (only templet.py and the two slideshow files are needed), which might be better for you than getting the full library.
It might be a lot easier to just generate a static web page with pictures than building a GUI for displaying pictures.
You can just generate a hmtl page, put the images on it and start your web browser with the newly created html file. this gives you some possibilities for layout as well.
If you just want a picture in the browser then muksie gave a working example.

Categories

Resources