Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Access and Change Color Channels using PIL?

I'm trying to access the RGB color channels of an image using PIL, and then change the color intensity of the color channel of the entire image at once.

When I say RGB color channels, here is an online example.

I don't understand if this has to be done on a pixel by pixel basis or not.

I image the logic of the code would look like this:

import PIL
from PIL import Image
image=Image.open("my_pic.gif")
image=image.convert('RGB')
# made up function
channel_values = image.get_channel_values()
#channel_values = i.e. (50, 100, 50)
# do some math function to transform channel_values
channel_values = (95,125,75)
image.update_channel_value('R',channel_values)
display(image.getchannel('R'))

This answer is the only one that comes close, but it is way too complicated for what I'm trying to do.

I've searched the PIL docs, etc. for a couple of hours but can't seem to get anywhere.

Here's how far I've gotten:

import PIL
from PIL import Image
image=Image.open("my_pic.gif")
image=image.convert('RGB')
display(image.getchannel('R'))

The problem is the image.getchannel() only returns a grey/black & white image.

I not only want to access the color channel value, I want to change it too.

like image 857
Python_Learner_DK Avatar asked Dec 13 '19 10:12

Python_Learner_DK


People also ask

Is PIL and Pillow the same?

What is PIL/Pillow? PIL (Python Imaging Library) adds many image processing features to Python. Pillow is a fork of PIL that adds some user-friendly features.


1 Answers

If, for example, you want to multiply all pixels in the red channel by 1.2, and all the green pixels by 0.9, you have several options....


Split the image into Red, Green and Blue channels, upscale the Red channel and recombine:

from PIL import Image
im = Image.open('image.jpg').convert('RGB')

# Split into 3 channels
r, g, b = im.split()

# Increase Reds
r = r.point(lambda i: i * 1.2)

# Decrease Greens
g = g.point(lambda i: i * 0.9)

# Recombine back to RGB image
result = Image.merge('RGB', (r, g, b))

result.save('result.png')

See "Processing Individual Bands" here.


Or, use a matrix to transform the channels:

from PIL import Image 

# Open image 
im = Image.open('image.jpg') 

# Make transform matrix, to multiply R by 1.1, G by 0.9 and leave B unchanged
# newRed   = 1.1*oldRed  +  0*oldGreen    +  0*oldBlue  + constant
# newGreen = 0*oldRed    +  0.9*OldGreen  +  0*OldBlue  + constant
# newBlue  = 0*oldRed    +  0*OldGreen    +  1*OldBlue  + constant
Matrix = ( 1.1,   0,  0, 0, 
           0,   0.9,  0, 0, 
           0,     0,  1, 0) 

# Apply transform and save 
im = im.convert("RGB", Matrix) 
im.save('result.png') 

Or, convert to Numpy array, do some processing and convert back to PIL Image:

from PIL import Image 

# Open image 
im = Image.open('image.jpg') 

# Make into Numpy array of floats
na = np.array(im).astype(np.float)

# Multiply all red values by 1.1
na[...,0] *= 1.1

# Reduce green values
na[...,1] *= 0.9

# You may want to use "np.clip()" here to ensure you don't exceed 255

# Convert Numpy array back to PIL Image and save
pi = Image.fromarray(na.astype(np.uint8))
pi.save('result.png') 

This option has the added benefit that the Numpy arrays can be intermixed and processed with OpenCV, scikit-image, vips, wand and other libraries to get MUCH more sophisticated processing done - morphology, granulometry, video processing, SIFT, object tracking...

like image 83
Mark Setchell Avatar answered Dec 05 '22 22:12

Mark Setchell