简体   繁体   中英

PIL putpixel does nothing

At high school, we are beginning with Python and we are asked to program a small image processor using the Pillow module. The goal is to open a file, then choose a filter.

The first filter I tried to program is to change the color temperature. But there's also a minimalist interface with tkinter that shows some buttons and it works without problems.

Here is the function that opens the file

def Ouvrir():
    fichier = askopenfilename(title="Ouvrir une image",filetypes=[('jpg files','.jpg'),('all files','.*')])
    global img
    img =Image.open(fichier)
    l, h = img.size
    img.show()   #visualisation de l'image

    global img2
    img2 = img #img2 est une copie de img

It creates a global img image object that is loaded from the file. Then l and h are loaded with the width and height of the image.

Another global object image img2 is created for the output image, and it is a copy of img .

Then here is the function that processes the image

def filtreTC(): #Filtre permettant de changer la température de couleur
    coef = sliderTC.get() / 100 #On récupère le coefficient à partir de l'échelle. Le coefficient compris entre -1 et 1: -1 = froid (image bleu-vert), 0 = neutre, 1 = chaud (image orangée)
    fenTC.destroy() #On ferme la fenêtre
    if(coef <= 0): #Calcul des coefficients rouges, verte et bleus
        coefR = 1 + coef
        coefV = 1 + (coef / 2)
        coefB = 1
    else:
        coefR = 1
        coefV = 1 - (coef / 2)
        coefB = 1 - coef
    for y in range(0, h, 1):
        for x in range(0, l, 1):
            r, v, b = img.getpixel((x, y))
            r = int(float(r * coefR))
            v = int(float(v * coefV))
            b = int(float(b * coefB))
            img2.putpixel((x, y), (r, v, b))
    img2.show()

It's a loop that scans through the whole image and takes a pixel from img , multiply its rgb values by their corresponding coefficients, then puts that pixel in img2

The problem is that it doesn't work. It doesn't give any errors, but when it shows img2 it's the same as img1 , like the putpixel function didn't do anything.

I checked a lot of things so I know the problem is neither the rgb values nor the x/y coordinates.

  • I tried replacing img2 = img by img2 = Image.new("RGB", (l, h)) and what I get is a black image.
  • I then tried replacing img2.putpixel((x, y), (r, v, b)) by img2.putpixel((100, 100), (127, 127, 127)) so that way I should get a grey pixel near the top left corner. But I still got a black image.

I then tried to remove

global img2
img2 = img

from the function that opens a file and put

img2 = Image.new("RGB", (l, h))

right after fenTC.destroy() and I got this:

Exception in Tkinter callback
Traceback (most recent call last):
  File "e:\xxxx\programmes\anaconda\lib\tkinter\__init__.py", line 1702, in __call__
    return self.func(*args)
  File "H:\ISN\Programmes\TP-image\projet.py", line 62, in filtreTC
    img2.show()
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\Image.py", line 2016, in show
    _show(self, title=title, command=command)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\Image.py", line 2876, in _show
    _showxv(image, **options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\Image.py", line 2881, in _showxv
    ImageShow.show(image, title, **options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\ImageShow.py", line 51, in show
    if viewer.show(image, title=title, **options):
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\ImageShow.py", line 75, in show
    return self.show_image(image, **options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\ImageShow.py", line 95, in show_image
    return self.show_file(self.save_image(image), **options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\ImageShow.py", line 91, in save_image
    return image._dump(format=self.get_format(image), **self.options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\Image.py", line 639, in _dump
    self.save(filename, format, **options)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\Image.py", line 1969, in save
    save_handler(self, fp, filename)
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\BmpImagePlugin.py", line 319, in _save
    (rawmode, stride, -1))])
  File "e:\xxxx\programmes\anaconda\lib\site-packages\PIL\ImageFile.py", line 512, in _save
    e.setimage(im.im, b)
SystemError: tile cannot extend outside image

Basically a lot of stuff I've got no idea what it means except:

SystemError: tile cannot extend outside image

I checked the x and y coordinates and they never went beyond the borders, so I don't understand what's this error.

I spent several hours to try figure out what the problem was and tried lots of different things but none of them worked. So I thought it was better to ask for help

Your problem is that you didn't create a copy . img2 = img creates another reference to the same object, not a new, separate image.

To create a actual copy, use the image.copy() method :

img2 = img.copy()

Next, I'd not use a loop and getpixel() / putpixel() combinations. You are doing double work for RGB values that appear more than once. If you use the image.point() method then you get to use your formula on each unique value in each band in the image, and leave the looping over all pixels to the library (much faster). It also makes the image copy for you!

You need to create a table; for values 0 through 255 for the R, G and B values, calculate the possible result, and put those 3 * 256 results in a long list:

coefG = [i * (1 - (coef / 2)) for i in range(256)]
if coef <= 0:
    coefR = [i * (1 + coef) for i in range(256)]
    coefB = list(range(256))
else:
    coefR = list(range(256))
    coefB = [i * (1 - coef) for i in range(256)]
img2 = img1.point(coefR + coefG + coefB)

The resulting table is used for each unique color value in the image.

You could also split the RGB image into separate bands, apply your different coefficient formulas to each separate band as a function then re-combine the bands into a new image:

r, g, b = img.split()
g = g.point(lambda i: i * (1 - (coef / 2)))
if coef <= 0:
    r = r.point(lambda i: i * (1 + coef))
else:
    b = b.point(lambda i: i * (1 - coef))
img = Image.merge('RGB', (r, g, b))

When creating an image filter, you'd really want to pass in the input image as a function argument rather than use globals. Pass in the coefficient as well:

def filtreTC(source_image, coef):
    coefG = [i * (1 - (coef / 2)) for i in range(256)]
    if coef <= 0:
        coefR = [i * (1 + coef) for i in range(256)]
        coefB = list(range(256))
    else:
        coefR = list(range(256))
        coefB = [i * (1 - coef) for i in range(256)]
    return source_image.point(coefR + coefG + coefB)

You can then store the result of the call in a global if you must, but the function can now stand on its own, and can be re-used anywhere that has a RGB PIL image object. You'd call the function with the slider value:

coef = sliderTC.get() / 100
fenTC.destroy()
img2 = filtreTC(img, coef)

Using the above on your profile image

提问者个人资料图片,芯片

with a coefficient of 0.75, gives us:

个人资料图片相同,但变成橙色

while -0.75 results in:

相同的个人资料图片,但现在变成蓝色

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM