NumPy, PIL adding an image

Go To


I'm trying to add two images together using NumPy and PIL. The way I would do this in MATLAB would be something like:

>> M1 = imread('_1.jpg');
>> M2 = imread('_2.jpg');
>> resM = M1 + M2;
>> imwrite(resM, 'res.jpg');

I get something like this:

alt text

Using a compositing program and adding the images the MATLAB result seems to be right.

In Python I'm trying to do the same thing like this:

from PIL import Image
from numpy import *

im1 ='/Users/rem7/Desktop/_1.jpg')
im2 ='/Users/rem7/Desktop/_2.jpg')

im1arr = asarray(im1)
im2arr = asarray(im2)

addition = im1arr + im2arr

resultImage = Image.fromarray(addition)'/Users/rem7/Desktop/a.jpg')

and I get something like this:

alt text

Why am I getting all those funky colors? I also tried using ImageMath.eval("a+b", a=im1, b=im2), but I get an error about RGB unsupported.

I also saw that there is an Image.blend() but that requires an alpha.

What's the best way to achieve what I'm looking for?

Source Images (images have been removed):

alt text alt text

Humm, OK, well I added the source images using the add image icon and they show up when I'm editing the post, but for some reason the images don't show up in the post.

(images have been removed) 2013 05 09

2009-02-08 01:15
by rem7


As everyone suggested already, the weird colors you're observing are overflow. And as you point out in the comment of schnaader's answer you still get overflow if you add your images like this:


The reason for this overflow is that your NumPy arrays (im1arr im2arr) are of the uint8 type (i.e. 8-bit). This means each element of the array can only hold values up to 255, so when your sum exceeds 255, it loops back around 0:

>>>array([255,10,100],dtype='uint8') +  array([1,10,160],dtype='uint8')
array([ 0, 20,  4], dtype=uint8)

To avoid overflow, your arrays should be able to contain values beyond 255. You need to convert them to floats for instance, perform the blending operation and convert the result back to uint8:

im1arrF = im1arr.astype('float')
im2arrF = im2arr.astype('float')
additionF = (im1arrF+im2arrF)/2
addition = additionF.astype('uint8')

You should not do this:

addition = im1arr/2 + im2arr/2

as you lose information, by squashing the dynamic of the image (you effectively make the images 7-bit) before you perform the blending information.

MATLAB note: the reason you don't see this problem in MATLAB, is probably because MATLAB takes care of the overflow implicitly in one of its functions.

2009-02-08 17:05
by Ivan
Thanks, your explanation was very clear - rem7 2009-02-09 16:20
Why 'float'? A 'uint16' would be sufficient - jfs 2009-02-10 22:11
There was no rational reason for choosing float, uint16 would have been enough indeed - Ivan 2009-02-11 09:02


Using PIL's blend() with an alpha value of 0.5 would be equivalent to (im1arr + im2arr)/2. Blend does not require that the images have alpha layers.

Try this:

from PIL import Image
im1 ='/Users/rem7/Desktop/_1.jpg')
im2 ='/Users/rem7/Desktop/_2.jpg')
2009-02-08 03:58
by Paul
this is especially nice for getting the job done without dragging in numpy - DarenW 2009-05-12 02:30


It seems the code you posted just sums up the values and values bigger than 256 are overflowing. You want something like "(a + b) / 2" or "min(a + b, 256)". The latter seems to be the way that your Matlab example does it.

2009-02-08 01:28
by schnaader
Yes, matlab clamps values when doing arithmetic on uint8 values (e.g. it implicitly does the equivalent to max(double(a)+double(b),256) ) - Mr Fooz 2009-02-08 01:58
When I try to do max(im1arr+im2arr,256) I get the error: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"

I do (im1arr+im2arr)/2 I get funky colors, only dimmer, max value 127 so I did: addition=(im1arr/2)+(im2arr/2) and that seems to work - rem7 2009-02-08 03:02

Don't you actually want min(a + b, 256) because if you get a value over 256 you want to clamp the value at 256 (you don't want to take a value over 256). In any case, to fix the error @rem7 pointed out, you can do additionF = np.minimum(im1arrF + im2arrF, 256)Vincent 2016-06-29 05:12


To clamp numpy array values:

>>> c = a + b
>>> c[c > 256] = 256
2009-02-08 09:54
by jfs
It assumes that type of elements is larger than uint8 - jfs 2009-02-10 22:09


Your sample images are not showing up form me so I am going to do a bit of guessing.

I can't remember exactly how the numpy to pil conversion works but there are two likely cases. I am 95% sure it is 1 but am giving 2 just in case I am wrong. 1) 1 im1Arr is a MxN array of integers (ARGB) and when you add im1arr and im2arr together you are overflowing from one channel into the next if the components b1+b2>255. I am guessing matlab represents their images as MxNx3 arrays so each color channel is separate. You can solve this by splitting the PIL image channels and then making numpy arrays

2) 1 im1Arr is a MxNx3 array of bytes and when you add im1arr and im2arr together you are wrapping the component around.

You are also going to have to rescale the range back to between 0-255 before displaying. Your choices are divide by 2, scale by 255/array.max() or do a clip. I don't know what matlab does

2009-02-08 01:32
by hacken
Are the images still not showing? I edited the question and after that, it works here - schnaader 2009-02-08 01:34
works for me now. It definitely looks like a wrapping/saturation issue. It would also be nice if you posted your source images - hacken 2009-02-08 01:53
I think the pil conversion does make it a MxNx3 since im1arr.shape prints this: (2477, 3700, 3). Option two seems to be correct - rem7 2009-02-08 03:10
It seems that I had to divide both images first before I can add them, even if I do a clip(min=0, max=255) on the result, the overflow already happened so the funky colors are still there - rem7 2009-02-08 03:29
The alternative to dividing first is to cast from a byte array over to an int (or short) array and then do your math - hacken 2009-02-08 05:56