1

I need to create a video out of sequence of images in python. I found this code online that works fine but I am having a small problem with the ordering of the images when being read in python. Even though the ordering in the folder is ok. E.x frame100.jpg , frame101.jpg , frame102.jpg,....., frame1000, frame1001, .... when I read them with python inside the loop , after debuging I see the following 'frame100.jpg', 'frame1000.jpg', 'frame1001.jpg', 'frame1002.jpg',.....,frame101,frame1010 , frame1011....

This is the code

def images_to_video():

image_folder = 'data_out'


images = [img for img in os.listdir(image_folder) if img.endswith(".jpg")]
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape

video = cv2.VideoWriter('project.avi',cv2.VideoWriter_fourcc(*'DIVX'), 15, (width,height))


for image in images:
    video.write(cv2.imread(os.path.join(image_folder, image)))


cv2.destroyAllWindows()
video.release()
ababak
  • 1,685
  • 1
  • 11
  • 23
Aris
  • 31
  • 1
  • 6

3 Answers3

5

You need to sort the filenames using the natural sorting that knows how to sort the numbers:

import re

def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
    return [
        int(text)
        if text.isdigit() else text.lower()
        for text in _nsre.split(s)]

sorted_images = sorted(images, key=natural_sort_key)

ababak
  • 1,685
  • 1
  • 11
  • 23
2

First of all, files in the folder are not "ordered" in any way on the OS level (at least in the most popular operating systems I am aware of). If you see them "ordered" in some file browser, it is purely done by the file browser app itself (finder/file explorer/etc) but python does not use it.

Second, if you read the documentation of os.listdir it is clearly specified that files are returned in an arbitrary order. You should order them yourself.

Third, your files are named without zero padding. This means that if you sort the file names in alphabetical order (that is the default for strings in python), you will get the order you specified above (frame101.jpg, frame1010.jpg). If they were zero padded, you could get the right order even if sorted as strings (frame0101.jpg, frame0102.jpg, ..., frame1010.jpg).

The solution is the extract the frame number from the file names, convert them to integer, and sort the file names based on this number. The simplest way to achieve this is:

sorted_images = sorted(images, key=lambda x:int(x[5:-4]))

where 5 is the length of the prefix frame and -4 is the length of .jpg, the prefix and suffix I wish to cut off from the filename. Check out python slicing and documentation of the key parameter of sorted for more info.

MrTJ
  • 13,064
  • 4
  • 41
  • 63
1

You're not explicitly ordering the frames, but are relying on os.listdir to order it for you. This may cause a problem because there is no guarantee the list will be in alphabetical order.

Reference: os.listdir() Documentation

Return a list containing the names of the entries in the directory given by path. The list is in arbitrary order

Solution 1

Explicitly order your list before processing, e.g.

images = images.sort()

or

for image in images.sort():

However, to do this you have to modify the names of your file first, or it would sort like 100 > 1001 > 1002 ... 101 > 1010 like you described. To do that, you can e.g. add a zero before those filenames fewer than 1000:

images = [img if len(img)==13 else img[:7] + "0" + img[-7:] for img in images]

Solution 2

If you're sure ALL the files are in the format of "frame + number + .jpg" then you can do this:

images = ["frame" + str(i) + ".jpg" for i in range(100, 1234)] ## replace 1234 with (last frame number + 1)
Mr.K
  • 559
  • 4
  • 9