Home > database >  Why does this for loop have an i counter that jumps from 1 to 10 when appending images on Python? PI
Why does this for loop have an i counter that jumps from 1 to 10 when appending images on Python? PI

Time:01-03

I have the following images at some specific path:

0.png
1.png
2.png
3.png
4.png
5.png
6.png
7.png
8.png
9.png
10.png
11.png
12.png
13.png
14.png
15.png
16.png
17.png
18.png
19.png
20.png
21.png
22.png
23.png
24.png
25.png
26.png

And I want to use the following program (which is located at the same specific path the images are located) to convert those images to a single gif file

from PIL import Image
import glob

# Create the frames
frames = []
imgs = glob.glob("*.png")
for i in imgs:
    new_frame = Image.open(i)
    frames.append(new_frame)
    print(i)

# Save into a GIF file that loops forever
frames[0].save('png_to_gif.gif', format='GIF',
               append_images=frames[1:],
               save_all=True,
               duration=160, loop=0)

After running the program above I got this output:

0.png
1.png
10.png
11.png
12.png
13.png
14.png
15.png
16.png
17.png
18.png
19.png
2.png
20.png
21.png
22.png
23.png
24.png
25.png
26.png
3.png
4.png
5.png
6.png
7.png
8.png
9.png

How can I assure that the order in which the program will append the images will be the same order in which they were named?

CodePudding user response:

Computer does not sort alfabetically but lexicographically.

"10" < "2", cause program looks on signs from left to right (first compare 1 with 2 if it is equal it stops and yell that 10 is smaller). that's why i jumps like that.

It would be better if every file have the same length (001.png, 002.png etc). If you prefer not to do that, you can sort them remembering that length is crucial (first check length, then if number1 < number2)

CodePudding user response:

The filenames you're getting appear to be ordered lexicographically, as strings. That's why longer strings that start with small numerals sort ahead of shorter strings with larger numbers. It's just like how ah sorts ahead of b in alphabetical order.

You want the numerical part of the string to be ordered numerically, so that 2 comes before 10. To do that, you'll need to apply your own sorting logic.

Try something like this:

import re # put this at the top of the file

def keyfunc(filename):
    prefix, numeral, suffix = re.split(r'(\d )', filename, maxsplit=1)
    return parts[0], int(parts[1]), parts[2]

imgs = glob.glob("*.png")
for i in sorted(imgs, key=keyfunc):
    ...

An alternative solution might be to rename the files that have only one digit in their names to have a leading zero. That is, 1.png should become 01.png and so on. That way the sorting that's being applied (by either glob.glob, or your filesystem) will order everything correctly (because 02 correctly sorts before 10 even lexicographically).

CodePudding user response:

I decided to modify the code this way, it seems to work for the moment, if there's a way in which I can just create an array to store only the filenames that end with .png for a given path, it's welcome:

from PIL import Image


# Create the frames
frames = []
imgs = ["0.png", "1.png", "2.png", "3.png", "4.png", "5.png", "6.png", "7.png", "8.png", "9.png", "10.png", "11.png", "12.png", "13.png", "14.png", "15.png", "16.png", "17.png", "18.png", "19.png", "20.png", "21.png", "22.png", "23.png", "24.png", "25.png", "26.png"]
for i in imgs:
    new_frame = Image.open(i)
    frames.append(new_frame)
    print(i)

# Save into a GIF file that loops forever
frames[0].save('png_to_gif.gif', format='GIF',
               append_images=frames[1:],
               save_all=True,
               duration=180, loop=0)
  • Related