Давид Шико
Давид Шико

Reputation: 417

Python pillow make gif from pixels

enter image description here

I have the image and a list of blue pixels. I want to iterate over the blue pixels, change them to red color and make gif from that. So it must be a line that consequently changing color from blue to red, but something going wrong

im = Image.open(r"test2.png")
pixels = im.load()
images = []
blues = get_sorted_blues()  # See func below

for x, y in blues:
     ...:     pixels[x, y] = (255, 0, 0)
     ...:     images.append(im)

images[0].save('result.gif',
     ...:                save_all=True,
     ...:                append_images=images[1:],
     ...:                duration=100,
     ...:                loop=0)

def get_sorted_blues():
    ...:     blues = []
    ...:     for x in range(im.width):
    ...:         for y in range(im.height):
    ...:             if pixels[x, y] == (0, 0, 255):
    ...:                 blues.append([x, y])
    ...:     return sorted(blues)

result.gif it is just a red line, without any animation

Upvotes: 2

Views: 569

Answers (2)

Adrian Shum
Adrian Shum

Reputation: 40036

So the problem is you failed to make it animated?

In your loop, you keep updating the same Image and put that same Image into the list, which means at the end, all entries in the list is pointing to the same Image, which is all red. In the for loop, instead of images.append(im), do images.append(im.copy()).

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207465

There are lots of ways to make the blue pixels red - and using a for loop is way down the list in terms of performance, readability, maintainability.


Here's one using a "Colour Matrix" to swap the red and blue channels:

from PIL import Image

# Open image
im = Image.open('lines.png')

# Define color matrix to swap the red and blue channels
# This says:
# New red   = 0*old red + 0*old green + 1*old blue + 0offset
# New green = 0*old red + 1*old green + 0*old blue + 0offset
# New blue  = 1*old red + 0*old green + 0*old blue + 0offset
Matrix = ( 0, 0, 1, 0, 
           0, 1, 0, 0, 
           1, 0, 0, 0)    

# Apply matrix
result = im.convert("RGB", Matrix)

enter image description here

This is around 40x faster than for loops. It takes 1.07ms on my machine versus 40ms using for loops.


Here's one using Numpy to find blue pixels and make them red:

import numpy as np
from PIL import image

# Open image and make Numpy version
im = Image.open('lines.png')
na = np.array(im)

# Make all blue pixels red
na[ np.all(na[:,:]==[0,0,255], axis=2) ] = [255,0,0] 

# Convert back to PIL Image
result = Image.fromarray(na)

This is around 8x faster at 5ms.


Here's one using Numpy to reverse the RGB ordering to BGR:

import numpy as np
from PIL import image

# Open image and make Numpy version
im = Image.open('lines.png')
na = np.array(im)

# Reverse channel ordering i.e. RGB -> BGR
BGR = na[...,::-1] 

# Convert back to PIL Image
result = Image.fromarray(BGR)

This is around 9x faster at 4.4ms.


Here's one where we use PIL to split the image into its constituent RGB channels and then merge them back in the reverse order:

from PIL import Image

# Open image
im = Image.open('lines.png')

# Split into R, G, B channels
R, G, B = im.split()

# Recombine in B, G, R order
result = Image.merge('RGB',(B,G,R))

This is around 100x faster at 371 microseconds.

Upvotes: 1

Related Questions