Python ImageIO Gif Set Delay Between Frames - python

I am using ImageIO: https://imageio.readthedocs.io/en/latest/userapi.html , and I want to know how to set delay between frames in a gif.
Here are the relevant parts of my code.
import imageio
. . .
imageio.mimsave(args.output + '.gif', ARR_ARR)
where ARR_ARR is an array of numpy uint8 2d array of couplets.
To be clear, I have no problem writing the gif. I cannot, however, find any clarification on being able to write the amount of delay between frames.
So, for example, I have frames 0 ... 9
They always play at the same rate. I would like to be able to control the number of milliseconds or whatever unit between frames being played.

Found it using imageio.help("GIF") you would pass in something like
imageio.mimsave(args.output + '.gif', ARR_ARR, fps=$FRAMESPERSECOND)
And that seems to work.

Related

set imageio compression level in python

I'm using imageio in Python to read in jpg images and write them as a gif, using something resembling the code below.
import imageio
with imageio.get_writer('mygif.gif', mode='I') as writer:
for filename in framefiles: # iterate over names of jpg files I want to turn into gif frames
frame = imageio.imread(filename)
writer.append_data(frame)
I'm noticing that the image quality in the gifs I produce is quite poor; I suspect this is due to some form of compression. Is there a way to tell imageio not to use any compression? Or maybe a way to do this with opencv instead?
Real problem is that GIF can display only 256 colors (8-bits color) so it has to reduce 24-bits colors (RGB) to 256 colors or it has emulate more colors using dots with different colors - ditherring.
As for options:
Digging in source code I found that it can get two parameters quantizer, palettesize which can control image/animation quality. (There is also subrectangles to reduce file size)
But there are two plugins for GIF which use different modules Pillow or FreeImage and they need different value for quantizer
PIL needs integer 0, 1 or 2.
FI needs string 'wu' or 'nq' (but later it converts it to integer 0 or 1)
They also keep these values in different way so if you want get current value or change it after get_writer() then you also need different code.
You can select module with format='GIF-PIL' or format='GIF-FI'
with imageio.get_writer('mygif.gif', format='GIF-PIL', mode='I',
quantizer=2, palettesize=32) as writer:
print(writer)
#print(dir(writer))
#print(writer._writer)
#print(dir(writer._writer))
print('quantizer:', writer._writer.opt_quantizer)
print('palette_size:', writer._writer.opt_palette_size)
#writer._writer.opt_quantizer = 1
#writer._writer.opt_palette_size = 256
#print('quantizer:', writer._writer.opt_quantizer)
#print('palette_size:', writer._writer.opt_palette_size)
with imageio.get_writer('mygif.gif', format='GIF-FI', mode='I',
quantizer='nq', palettesize=32) as writer:
print(writer)
#print(dir(writer))
print('quantizer:', writer._quantizer)
print('palette_size:', writer._palettesize)
#writer._quantizer = 1
#writer._palettesize = 256
#print('quantizer:', writer._quantizer)
#print('palette_size:', writer._palettesize)
I tried to create animations with different settings but they don't look much better.
I get better result using external program ImageMagick in console/terminal
convert image*.jpg mygif.gif
but still it wasn't as good as video or static images.
You can run it in Python
os.system("convert image*.jpg mygif.gif")
subprocess.run("convert image*.jpg mygif.gif", shell=True)
Or you can try to do it with module Wand which is a wrapper on ImageMagick
Source code: GifWriter in pillowmulti.py and in freeimagemulti.py
* wu - Wu, Xiaolin, Efficient Statistical Computations for Optimal Color Quantization
* nq (neuqant) - Dekker A. H., Kohonen neural networks for optimal color quantization
Doc: GIF-PIL Static and animated gif (Pillow), GIF-FI Static and animated gif (FreeImage)

Efficient way to import a large amount of images from a video in python

I am currently working on making a dataset for a computer vision problem. I wanted to add some data to the previous ones I had. So I wanted to get around ~3000 frames from 2 different videos.
I used openCV because I knew the capture feature but I'm not sure about this because my memory is really exploding. I was using pickle file for the previous dataset that was already processed and I had no problem having that much information with my memory. Maybe my code is horrible without noticing it...
Here is my code to get around 3000 frames from the videos :
import cv2
video_name1 = "videosDataset/AMAZExNHORMS2019_Lo-res.mp4"
video_name2 = "videosDataset/CAMILLATHULINS2019_Lo-res.mp4"
def getAllFrames(videoName):
video_name = videoName
property_id =int(cv2.CAP_PROP_FRAME_COUNT)
cap = cv2.VideoCapture(video_name) #video_name is the video being called
frames = []
length = int(cv2.VideoCapture.get(cap, property_id))
print(length)
minL = int(length/2)
maxL = int(2*length/3)
print(minL,maxL)
for i in range(minL,maxL):
cap.set(1,i); # Where frame_no is the frame you want
ret, frame = cap.read() # Read the frame
frames.append(frame)
print(str(round((i-minL)/(maxL-minL)*100, 2))+'%')
return frames
frames1 = getAllFrames(video_name1)
I would like to know if there is a better way to do this. Thank you
The problem here is the compresion - when read, each frame is stored as numpy array which is rather expensive. For example - one RGB frame of 1280 x 720 pixels is about 200 kB in jpg format, 1.2 MB in png format, 2.7 MB when stored in numpy uint8 array and 22 MB when stored in numpy float64 array.
Easiest solution is to store each frame to disk as jpg image (e.g. by cv2.imwrite) instead of creating an array with all frames.
Assuming that by making a dataset, you mean that want to save all the frames individually for use in the dataset, the easiest option would probably be to use a tool like ffmpeg to do so. See here for an example to do so. Ffmpeg will support a number of image file formats, probably including the format you want to save the image in.

Re-plot graph as a video

I have a dataset which needs to be re- plotted in real time as a video. There are 1000 data points in 1 second. After this, I would also like to replot the same video 1/10th of the speed. An example is shown below. I did this in another software, which had an option for doing this in a GUI interface.
Is there a way to do this in R or Python? I looked into some libraries like ‘animation’ in R but could not get exactly what I wanted.
Here's an example in R using the animation package:
library(animation)
set.seed(2)
dat = data.frame(x=1:50, y=cumsum(rnorm(50)))
# Two times through the animation, once fast, once slow
ani.options(interval=rep(c(1/nrow(dat),1/nrow(dat)*10), each=nrow(dat)))
saveGIF(for(i in 1:(2*nrow(dat))) {
plot(dat$x[1:(i %% nrow(dat))], dat$y[1:(i %% nrow(dat))], type="l",
ylim=range(dat$y), xlim=range(dat$x), xlab="Time", ylab="Value")
}, "my_movie.gif")

my program reduces music speed by 50% but only in one channel

I am using the wave library in python to attempt to reduce the speed of audio by 50%. I have been successful, but only in the right channel. in the left channel it is a whole bunch of static.
import wave,os,math
r=wave.open(r"C:\Users\A\My Documents\LiClipse Workspace\Audio
compression\Audio compression\aha.wav","r")
w=wave.open(r"C:\Users\A\My Documents\LiClipse Workspace\Audio
compression\Audio compression\ahaout.wav","w")
frames=r.readframes(r.getnframes())
newframes=bytearray()
w.setparams(r.getparams())
for i in range(0,len(frames)-1):
newframes.append(frames[i])
newframes.append(frames[i])
w.writeframesraw(newframes)
why is this? since I am just copying and pasting raw data surely I can't generate static?
edit: I've been looking for ages and I finally found a useful resource for the wave format: http://soundfile.sapp.org/doc/WaveFormat/
If I want to preserve stereo sound, it looks like I need to copy the actual sample width of 4 twice. This is because there are two channels and they take up 4 bytes instead of 2.
`import wave
r=wave.open(r"C:\Users\A\My Documents\LiClipse Workspace\Audio
compression\Audio compression\aha.wav","r")
w=wave.open(r"C:\Users\A\My Documents\LiClipse Workspace\Audio
compression\Audio compression\ahaout.wav","w")
frames=r.readframes(r.getnframes())
newframes=bytearray()
w.setparams(r.getparams())
w.setframerate(r.getframerate())
print(r.getsampwidth())
for i in range(0,len(frames)-4,4):
newframes.append(frames[i])
newframes.append(frames[i+1])
newframes.append(frames[i+2])
newframes.append(frames[i+3])
newframes.append(frames[i])
newframes.append(frames[i+1])
newframes.append(frames[i+2])
newframes.append(frames[i+3])
w.writeframesraw(newframes)`
Edit 2:
Okay I have no idea what drove me to do this but I am already enjoying the freedoms it is giving me. I chose to copy the wav file into memory, edit the copy directly, and write it to an output file. I am incredibly happy with the results. I can import a wav, repeat the audio once, and write it to an output file, in only 0.2 seconds. Reducing the speed by half times now takes only 9 seconds instead of the 30+ seconds with my old code using the wav plugin :) here's the code, still kind of un-optimized i guess but it's better than what it was.
import struct
import time as t
t.clock()
r=open(r"C:/Users/apier/Documents/LiClipse Workspace/audio editing
software/main/aha.wav","rb")
w=open(r"C:/Users/apier/Documents/LiClipse Workspace/audio editing
software/main/output.wav","wb")
rbuff=bytearray(r.read())
def replacebytes(array,bites,stop):
length=len(bites)
start=stop-length
for i in range(start,stop):
array[i]=bites[i-start]
def write(audio):
w.write(audio)
def repeat(audio,repeats):
if(repeats==1):
return(audio)
if(repeats==0):
return(audio[:44])
replacebytes(audio, struct.pack('<I', struct.unpack('<I',audio[40:44])
[0]*repeats), 44)
return(audio+(audio[44:len(audio)-58]*(repeats-1)))
def slowhalf(audio):
buff=bytearray()
replacebytes(audio, struct.pack('<I', struct.unpack('<I',audio[40:44])
[0]*2), 44)
for i in range(44,len(audio)-62,4):
buff.append(audio[i])
buff.append(audio[i+1])
buff.append(audio[i+2])
buff.append(audio[i+3])
buff.append(audio[i])
buff.append(audio[i+1])
buff.append(audio[i+2])
buff.append(audio[i+3])
return(audio[:44]+buff)
rbuff=slowhalf(rbuff)
write(rbuff)
print(t.clock())
I am surprised at how small the code is.
Each of the elements returned by readframes is a single byte, even though the type is int. An audio sample is typically 2 bytes. By doubling up each byte instead of each whole sample, you get noise.
I have no idea why one channel would work, with the code shown in the question it should be all noise.
This is a partial fix. It still intermixes the left and right channel, but it will give you an idea of what will work.
for i in range(0,len(frames)-1,2):
newframes.append(frames[i])
newframes.append(frames[i+1])
newframes.append(frames[i])
newframes.append(frames[i+1])
Edit: here's the code that should work in stereo. It copies 4 bytes at a time, 2 for the left channel and 2 for the right, then does it again to double them up. This will keep the channel data from interleaving.
for i in range(0, len(frames), 4):
for _ in range(2):
for j in range(4):
newframes.append(frames[i+j])

OpenCV Python cv2.VideoCapture.read() returns wrong images

I have just started using OpenCV 2.4.8.2 in Python 2.7.6 on a MacBook Pro Retina running OS X 10.9.2. My main goal is to make a video file using a few NumPy-arrays. I would also like to do the inverse: decompose a video into separate frames (and consequently in NumPy-arrays).
To make a video, I use the following piece of code:
import cv2
# Composes a movie from separate frames.
videoMaker = cv2.VideoWriter("videoTest.mov", cv2.cv.CV_FOURCC('m', 'p', '4', 'v'), 1, (256, 256))
if (videoMaker.isOpened()):
videoMaker.write(cv2.imread("videoTestFrame0.jpg"))
videoMaker.write(cv2.imread("videoTestFrame1.jpg"))
videoMaker.write(cv2.imread("videoTestFrame2.jpg"))
videoMaker.release()
This piece of code seems to work fine - videoTest.mov is created, can be played in Quicktime, is 3 seconds long and consists of 3 different frames.
To load a video, I have put directly under the above code piece:
# Decomposes a movie into separate frames.
videoReader = cv2.VideoCapture("videoTest.mov")
count = 0
while True:
gotImage, image = videoReader.read()
if (gotImage):
cv2.imwrite("videoTestDecomposedFrame%d.jpg" %count, image)
count += 1
else:
break
The problem: When inspecting the 3 decomposed frames, the first frame of the movie (videoTestFrame0.jpg) is not one of them, while the last frame of the movie (videoTestFrame2.jpg) is stored twice.
How can I fix this problem and retrieve videoTestFrame0.jpg from the video without having to modify it by putting in this first frame twice? Might something be wrong with cv2.VideoCapture.read()? I have tried saving the movie as videoTest.avi instead of videoTest.mov, but the behavior at decomposition is the same.
Thanks in advance for your kind help! Martijn

Categories

Resources