moski moski - 23 days ago 10
Python Question

Python PIL - function to divide blend two images?

EDIT: Code is working now, thanks to Mark and zephyr. zephyr also has two alternate working solutions below.

I want to divide blend two images with PIL. I found

ImageChops.multiply(image1, image2)
but I couldn't find a similar
divide(image, image2)
function.

Divide Blend Mode Explained (I used the first two images here as my test sources.)

Is there a built-in divide blend function that I missed (PIL or otherwise)?

My test code below runs and is getting close to what I'm looking for. The resulting image output is similar to the divide blend example image here: Divide Blend Mode Explained.

Is there a more efficient way to do this divide blend operation (less steps and faster)? At first, I tried using lambda functions in
Image.eval
and
ImageMath.eval
to check for black pixels and flip them to white during the division process, but I couldn't get either to produce the correct result.

EDIT: Fixed code and shortened thanks to Mark and zephyr. The resulting image output matches the output from zephyr's numpy and scipy solutions below.

# PIL Divide Blend test

import Image, os, ImageMath

imgA = Image.open('01background.jpg')
imgA.load()
imgB = Image.open('02testgray.jpg')
imgB.load()

# split RGB images into 3 channels
rA, gA, bA = imgA.split()
rB, gB, bB = imgB.split()

# divide each channel (image1/image2)
rTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=rA, b=rB).convert('L')
gTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=gA, b=gB).convert('L')
bTmp = ImageMath.eval("int(a/((float(b)+1)/256))", a=bA, b=bB).convert('L')

# merge channels into RGB image
imgOut = Image.merge("RGB", (rTmp, gTmp, bTmp))

imgOut.save('PILdiv0.png', 'PNG')

os.system('start PILdiv0.png')

Answer

There is a mathematical definition for the divide function here: http://www.linuxtopia.org/online_books/graphics_tools/gimp_advanced_guide/gimp_guide_node55_002.html

Here's an implementation with scipy/matplotlib:

import numpy as np
import scipy.misc as mpl

a = mpl.imread('01background.jpg')
b = mpl.imread('02testgray.jpg')

c = a/((b.astype('float')+1)/256)
d = c*(c < 255)+255*np.ones(np.shape(c))*(c > 255)

e = d.astype('uint8')

mpl.imshow(e)
mpl.imsave('output.png', e)

If you don't want to use matplotlib, you can do it like this (I assume you have numpy):

imgA = Image.open('01background.jpg')
imgA.load()
imgB = Image.open('02testgray.jpg')
imgB.load()

a = asarray(imgA)
b = asarray(imgB)
c = a/((b.astype('float')+1)/256)
d = c*(c < 255)+255*ones(shape(c))*(c > 255)
e = d.astype('uint8')

imgOut = Image.fromarray(e)
imgOut.save('PILdiv0.png', 'PNG')

Comments