Display interleaved image in python [duplicate] - python

So I have a set of data which I am able to convert to form separate numpy arrays of R, G, B bands. Now I need to combine them to form an RGB image.
I tried 'Image' to do the job but it requires 'mode' to be attributed.
I tried to do a trick. I would use Image.fromarray() to take the array to image but it attains 'F' mode by default when Image.merge requires 'L' mode images to merge. If I would declare the attribute of array in fromarray() to 'L' at first place, all the R G B images become distorted.
But, if I save the images and then open them and then merge, it works fine. Image reads the image with 'L' mode.
Now I have two issues.
First, I dont think it is an elegant way of doing the work. So if anyone knows the better way of doing it, please tell
Secondly, Image.SAVE is not working properly. Following are the errors I face:
In [7]: Image.SAVE(imagefile, 'JPEG')
----------------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/media/New Volume/Documents/My own works/ISAC/SAMPLES/<ipython console> in <module>()
TypeError: 'dict' object is not callable
Please suggest solutions.
And please mind that the image is around 4000x4000 size array.

rgb = np.dstack((r,g,b)) # stacks 3 h x w arrays -> h x w x 3
To also convert floats 0 .. 1 to uint8 s,
rgb_uint8 = (np.dstack((r,g,b)) * 255.999) .astype(np.uint8) # right, Janna, not 256

I don't really understand your question but here is an example of something similar I've done recently that seems like it might help:
# r, g, and b are 512x512 float arrays with values >= 0 and < 1.
from PIL import Image
import numpy as np
rgbArray = np.zeros((512,512,3), 'uint8')
rgbArray[..., 0] = r*256
rgbArray[..., 1] = g*256
rgbArray[..., 2] = b*256
img = Image.fromarray(rgbArray)
img.save('myimg.jpeg')

rgb = np.dstack((r,g,b)) # stacks 3 h x w arrays -> h x w x 3
This code doesnt create 3d array if you pass 3 channels. 2 channels remain.

Convert the numpy arrays to uint8 before passing them to Image.fromarray
Eg. if you have floats in the range [0..1]:
r = Image.fromarray(numpy.uint8(r_array*255.999))

Your distortion i believe is caused by the way you are splitting your original image into its individual bands and then resizing it again before putting it into merge;
`
image=Image.open("your image")
print(image.size) #size is inverted i.e columns first rows second eg: 500,250
#convert to array
li_r=list(image.getdata(band=0))
arr_r=np.array(li_r,dtype="uint8")
li_g=list(image.getdata(band=1))
arr_g=np.array(li_g,dtype="uint8")
li_b=list(image.getdata(band=2))
arr_b=np.array(li_b,dtype="uint8")
# reshape
reshaper=arr_r.reshape(250,500) #size flipped so it reshapes correctly
reshapeb=arr_b.reshape(250,500)
reshapeg=arr_g.reshape(250,500)
imr=Image.fromarray(reshaper,mode=None) # mode I
imb=Image.fromarray(reshapeb,mode=None)
img=Image.fromarray(reshapeg,mode=None)
#merge
merged=Image.merge("RGB",(imr,img,imb))
merged.show()
`
this works well !

If using PIL Image convert it to array and then proceed with the below, else using matplotlib or cv2 perform directly.
image = cv2.imread('')[:,:,::-1]
image_2 = image[10:150,10:100]
print(image_2.shape)
img_r = image_2[:,:,0]
img_g = image_2[:,:,1]
img_b = image_2[:,:,2]
image_2 = img_r*0.2989 + 0.587*img_g + 0.114*img_b
image[10:150,10:100,0] = image_2
image[10:150,10:100,1] = image_2
image[10:150,10:100,2] = image_2
plt.imshow(image,cmap='gray')

Related

Pillow: How to draw text with the inverse color of the underlying image? [duplicate]

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')
You are asking:
Is there a more efficient way to do this divide blend operation (less steps and faster)?
You could also use the python package blend modes. It is written with vectorized Numpy math and generally fast. Install it via pip install blend_modes. I have written the commands in a more verbose way to improve readability, it would be shorter to chain them. Use blend_modes like this to divide your images:
from PIL import Image
import numpy
import os
from blend_modes import blend_modes
# Load images
imgA = Image.open('01background.jpg')
imgA = numpy.array(imgA)
# append alpha channel
imgA = numpy.dstack((imgA, numpy.ones((imgA.shape[0], imgA.shape[1], 1))*255))
imgA = imgA.astype(float)
imgB = Image.open('02testgray.jpg')
imgB = numpy.array(imgB)
# append alpha channel
imgB = numpy.dstack((imgB, numpy.ones((imgB.shape[0], imgB.shape[1], 1))*255))
imgB = imgB.astype(float)
# Divide images
imgOut = blend_modes.divide(imgA, imgB, 1.0)
# Save images
imgOut = numpy.uint8(imgOut)
imgOut = Image.fromarray(imgOut)
imgOut.save('PILdiv0.png', 'PNG')
os.system('start PILdiv0.png')
Be aware that for this to work, both images need to have the same dimensions, e.g. imgA.shape == (240,320,3) and imgB.shape == (240,320,3).
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 &lt 255)+255*ones(shape(c))*(c &gt 255)
e = d.astype('uint8')
imgOut = Image.fromarray(e)
imgOut.save('PILdiv0.png', 'PNG')
The problem you're having is when you have a zero in image B - it causes a divide by zero. If you convert all of those values to one instead I think you'll get the desired result. That will eliminate the need to check for zeros and fix them in the result.

Array to Image conversion

I've been struggling for hours now with this tiny problem.
I've been trying to do some image modification. Here is the code snipet :
import numpy as np
from PIL import Image
#Conversion image array
img = Image.open('lena.jpg')
array = np.array(img)
def niv_de_gris(img):
height = len(img)
width = len(img[0])
#Creation tableau vide
new_img = ([[[0 for i in range(3)] for j in range(width)] for k in range(height)])
for i in range(height):
for j in range(width):
m = np.mean(img[i][j])
for k in range(3):
new_img[i][j][k] = int(m)
return np.array(new_img)
array_gris = niv_de_gris(array)
img_gris = Image.fromarray(array_gris) #problem is here !!
the first conversion works perfectly fine : it takes an image an converts it into an array. The program runs flowlessly, the image modification works, it sends me back an array of the image converted in gray levels.
Yet when I want to convert this array into an image to .show() it, i get this error :
Error screenshot
Can anybody help me figure this out pls?
Have a nice day!
Array to PIL may need to ensure pixel values are not 0-1 but 0-255, type is unit8 and then define the mode as 'RGB'.
Try using -
array_gris = niv_de_gris(array) * 255 #Skip this step if pixels are 0-255 already
array_gris = array_gris.astype(np.uint8)
img_gris = Image.fromarray(array_gris, mode='RGB')
That worked for me on a random image that I choose. Depending on the function and the image, I believe the only real important step is to ensure they are legit pixel values and that type is uint8.

How can I create a color image (RGB), after changing it into a 3 dimensional array of each pixel?

My issue is that I'm taking a sample image and trying to change specific RGB values to change the appearance. After getting the data for each pixel, saving it as a 3-dimensional array, I then try to turn it back into an image but I'm getting the following error.
I know that it wants 2-dimensional arrays to convert it back into an image, but I don't know how i can keep it in color and convert it back to an image. Any ideas?
from PIL import Image
import numpy as np
# Opens the image, giving it the call name im
img = Image.open("sample_images\sample_image_1.jpg")
# This function makes a list containing every RGB value of each pixel in sample_image_1
img_as_list = np.uint8(np.asarray(img))
print(img_as_list)
for x in img_as_list:
for rVal in x[0]:
if rVal <= 255:
rVal = 0
for bVal in x[1]:
if bVal <= 255:
bVal = 0
for gVal in x[2]:
if gVal <= 255:
gVal = 0
# Crate a new image from the list that i made from the picture
new_img = Image.fromarray(np.uint8(img_as_list),"L")
print(img_as_list)
new_img.show()
img.show()
Thanks
The solution here is luckily quite simple.
instead of
new_img = Image.fromarray(np.uint8(img_as_list),"L")
use
new_img = Image.fromarray(np.uint8(img_as_list))
The L means that it is expecting 8-bit pixels, black and white. So it would only need a 0-255 value for each pixel. Instead use another mode.
Instead of removing the parameter completely, you could instead use RGB or RGBA depending on the file type.
Check out this out for more modes.
Hope this helps!

Save Different Channels of Ycbcr as seperate images | Python

I need to apply some transformations to the individual channels of the Ycbcr color space.
I have an tiff format image as the source and I need to convert it to ycbcr color space. I have not been able to successfully save the different channels as separate images. I have been only able to extract the luminescence channel using this code:
import numpy
import Image as im
image = im.open('1.tiff')
ycbcr = image.convert('YCbCr')
B = numpy.ndarray((image.size[1], image.size[0], 3), 'u1', ycbcr.tobytes())
im.fromarray(B[:,:,0], "L").show()
Can someone pls help.
Thank you
Here is my code:
import numpy
import Image as im
image = im.open('1.tiff')
ycbcr = image.convert('YCbCr')
# output of ycbcr.getbands() put in order
Y = 0
Cb = 1
Cr = 2
YCbCr=list(ycbcr.getdata()) # flat list of tuples
# reshape
imYCbCr = numpy.reshape(YCbCr, (image.size[1], image.size[0], 3))
# Convert 32-bit elements to 8-bit
imYCbCr = imYCbCr.astype(numpy.uint8)
# now, display the 3 channels
im.fromarray(imYCbCr[:,:,Y], "L").show()
im.fromarray(imYCbCr[:,:,Cb], "L").show()
im.fromarray(imYCbCr[:,:,Cr], "L").show()
Simply use the .split() method to split the image into different channels (called bands in PIL). No need to use numpy.
(y, cb, cr) = ycbcr.split()
# y, cb and cr are all in "L" mode.
After you have done the transformations, use PIL.Image.merge() to combine them again.
ycbcr2 = im.merge('YCbCr', (y, cb, cr))

Averaging multiple images in python

I'm trying to average 300 .tif images with this code :
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=[filename for filename in allfiles if filename[-4:] in[".tif",".TIF"]]
# Assuming all images are the same size, get dimensions of first image
w,h = Image.open(imlist[0]).size
N = len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr = numpy.zeros((h,w,3),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr = numpy.array(Image.open(im),dtype=numpy.float)
arr = arr+imarr/N
# Round values in array and cast as 16-bit integer
arr = numpy.array(numpy.round(arr),dtype=numpy.uint16)
# Generate, save and preview final image
out = Image.fromarray(arr,mode="RGB")
out.save("Average.tif")
And it gives me a TypeError like that :
imarr = numpy.array(Image.open(im),dtype=numpy.float)
TypeError: float() argument must be a string or a number, not 'TiffImageFile'
I understand that it doesn't really like to put a TIF image in the numpy array (it also doesn't work with PNG images). What should I do ? Splitting each image into R, G and B arrays to average and then merge everything seems too memory consuming.
It should work as is, checked right now with PIL (pillow 2.9.0) and numpy 1.9.2.

Categories

Resources