'Problem in image processing code using pillow?

I have tried to create a filter which transforms a picture.

Example: The first pixel takes the color from next one, and the last one takes the color from first one.

I used python 3.6.8 and the pillow library.

Here is my code:

from PIL import Image
from time import sleep

import random

size1=1024
size2=1024

img_1 = Image.new( 'RGB', (size1,size2), "black") # create a new black image
pixels = img_1.load() # create the pixel map
img_1.show()

for i in range(img_1.size[0]):    # for every col:
    for j in range(img_1.size[1]):    # For every row       
        pixels[i,j] = (random.randint(0,255), random.randint(0,255), random.randint(0,255)) # set the colour accordingly

img_1.show()

img_2=img_1
pixels2 = img_2.load()
for j in range(img_2.size[0]):
    for i in range(img_2.size[1]):
        if i<size1+1:
            pixels2[i,j] = pixels[i+1,j]
        else:
            pixels2[255,j] = pixels[0,j]
img_2.show()

And here is the error I get:

Traceback (most recent call last):
  File "C:\Users\spyrosamvra\Desktop\Image procesing\image_prosec.py", line 23, in <module>
    pixels2[i,j] = pixels[i+1,j]
IndexError: image index out of range

How can I fix this issue to properly transform the image as described above?



Solution 1:[1]

The bound check is wrong. You should check against size1-1, not size1+1. You furthermore hardcoded 255 as column in the else case, but size1 is 1024, so it makes more sense to just use i insteat:

pixels2 = img_2.load()
for j in range(img_2.size[0]):
    for i in range(img_2.size[1]):
        if i < size1-1:
            pixels2[i,j] = pixels[i+1,j]
        else:
            pixels2[i,j] = pixels[0,j]
img_2.show()

You can simplify this further by using a modulo expression:

pixels2 = img_2.load()
for j in range(img_2.size[0]):
    for i in range(img_2.size[1]):
        pixels2[i,j] = pixels[(i+1)%size1,j]
img_2.show()

The above will however still not work, since here you will first assign a value to a pixel that you will later use for a second copy operation. You can solve this by storing the first pixel in a variable, and then later set that variable, like:

pixels2 = img_2.load()
for j in range(img_2.size[0]):
    first = pixels2[0,j]
    for i in range(img_2.size[1]-1):
        pixels2[i,j] = pixels[i+1,j]
    pixels2[-1,j] = first
img_2.show()

For advanced image processing, it might however make more sense to use a library like opencv-python [PyPi].

Solution 2:[2]

There are better ways to roll an image than use for loops.

You can convert to a Numpy array and use Numpy's roll() documentation like this (untested):

from PIL import Image
import numpy as np

# Load image and make Numpy version
im = Image.open('start.png')
numpyIm = np.array(im)

# Roll image
rolled = np.roll(numpyIm,1,1). # change first 1 to alter distance, change second 1 to alter direction

# Convert Numpy image back to PIL Image
pilIm = Image.fromarray(rolled)

Or you can use PIL's example from here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Willem Van Onsem
Solution 2 Mark Setchell