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.
Related
I am making a program where you can chose an image and a color and it will invert only the pixels that match that color. I've been surfing stackoverflow and reddit for solutions but so far no luck.
I tried to do something like this first:
img = Image.open('past.png')
pixels = img.load()
for i in goodpixels:
ImageOps.invert(pixels[i])
AttributeError: 'tuple' object has no attribute 'mode'
No luck with that because ImageOps.invert only inverts full images. Next I tried to use ImageOps.polarize but realized that I couldn't use it because it takes greyscale thresholds not rgb values.
img = Image.open('past.png')
pixels = img.load()
for i in goodpixels:
ImageOps.solarize(img, threshold=pixels[i])
TypeError: '<' not supported between instances of 'int' and 'tuple'
This is my issue, I don't know if this is even possible. If it takes too much work I will probably abandon the project anyways because I'm just keeping myself occupied, and this isn't for marks/job.
Some more code:
def checkpixels():
img = Image.open('past.png')
height, width = img.size
img = img.convert('RGB')
targetcolor = input('What color do you want to search for, you can use RGB format or common names like \'red\', \'black\', e.t.c. Leave this blank to invert all pixels. ')
print('Processing image. This could take several minutes.')
isrgb = re.match(r'\d+, \d+, \d+|\(\d+, \d+, \d+\)', targetcolor)
if type(isrgb) == re.Match:
targetcolor = targetcolor.strip('()')
targetcolor = targetcolor.split(', ')
targetcolor = tuple(map(int, targetcolor))
print(str(targetcolor))
for x in range(width):
for y in range(height):
color = img.getpixel((y-1, x-1))
if color == targetcolor:
goodpixels.append((y-1, x-1))
else:
try:
targetcolor = ImageColor.getcolor(targetcolor.lower(), 'RGB')
print(targetcolor)
for x in range(width):
for y in range(height):
color = img.getpixel((y-1, x-1))
if color == targetcolor:
goodpixels.append((y-1, x-1))
except:
print('Not a valid color smh.')
return goodpixels
goodpixels = []
goodpixels = checkpixels()
Edit: I figured it out! Thank you to Mark Setchell for his incredible brain! I ended up using numpy to convert the image and target color to arrays, making an inverted copy of the image, and using numpy.where() to decide whether or not to switch out the pixels. I also plan on making the target color a range so the chosen color doesn't have to be so specific. All in all my code looks like this:
goodpixels = []
targetcolor = inputcolor()
img = Image.open('past.png')
invertimage = img.copy().convert('RGB')
invertimage = ImageOps.invert(invertimage)
invertimage.save('invert.png')
pastarray = np.array(img)
targetcolorarray = np.array(targetcolor)
pixels = img.load()
inverse = np.array(invertimage)
result = np.where((pastarray==targetcolorarray).all(axis=-1)[...,None], inverse, pastarray)
Image.fromarray(result.astype(np.uint8)).save('result.png')
Of course, inputcolor() is a function offscreen which just decides if the input is a color name or rgb value. Also I used import numpy as np in this example.
A problem that I had was that original my .where method looked like this:
result = np.where((pastarray==[0, 0, 0]).all(axis=-1)[...,None], inverse, pastarray)
This brought up the error: AttributeError: 'bool' object has no attribute 'all'
It turns out all I had to do was convert my color into an array!
Many libraries allow you to import an image into Python as a numpy array. PIL or opencv2 are well documented libraries for working with images:
pip install opencv2
Example numpy.where() selection, meeting a set criteria, in this case inverting all pixel values below THRESHOLD:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# cut off thereshold
THRESHOLD = 230
pixel_data = cv2.imread('filename.png')
pixel_data = np.where(pixel_data < THRESHOLD, 1/pixel_data, pixel_data)
# display the edited image using matplotlib
plt.imshow(pixel_data)
The numpy.where() function applies a condition to your numpy array. More details available on the: numpy official documentation
I am trying to use a dicom image and manipulate it using OpenCV in a Python environment. So far I have used the pydicom library to read the dicom(.dcm) image data and using the pixel array attribute to display the picture using OpenCV imshow method. But the output is just a blank window. Here is the snippet of code I am using at this moment.
import numpy as np
import cv2
import pydicom as dicom
ds=dicom.dcmread('sample.dcm')
cv2.imshow('sample image dicom',ds.pixel_array)
cv2.waitkey()
If i print out the array which is used here, the output is different from what i would get with a normal numpy array. I have tried using matplotlib imshow method as well and it was able to display the image with some colour distortions. Is there a way to convert the array into a legible format for OpenCV?
Faced a similar issue. Used exposure.equalize_adapthist() (source). The resulting image isn't a hundred percent to that you would see using a DICOM Viewer but it's the best I was able to get.
import numpy as np
import cv2
import pydicom as dicom
from skimage import exposure
ds=dicom.dcmread('sample.dcm')
dcm_sample=ds.pixel_array
dcm_sample=exposure.equalize_adapthist(dcm_sample)
cv2.imshow('sample image dicom',dcm_sample)
cv2.waitkey()
I have figured out a way to get the image to show. As Dan mentioned in the comments, the value of the matrix was scaled down and due to the imshow function, the output was too dark for the human eye to differentiate. So, in the end the only thing i needed to do was multiply the entire mat data with 128. The image is showing perfectly now. multiplying the matrix by 255 over exposes the picture and causes certain features to blow. Here is the revised code.
import numpy as np
import cv2
import pydicom as dicom
ds=dicom.dcmread('sample.dcm')
dcm_sample=ds.pixel_array*128
cv2.imshow('sample image dicom',dcm_sample)
cv2.waitkey()
I don't think that is a correct answer. It works for that particular image because most of your pixel values are in the lower range. Check this OpenCV: How to visualize a depth image. It is for c++ but easily adapted to Python.
This is the best way(in my opinion) to open image in opencv as a numpy array while perserving the image quality:
import numpy as np
import pydicom, os, cv2
def dicom_to_numpy(ds):
DCM_Img = ds
rows = DCM_Img.get(0x00280010).value #Get number of rows from tag (0028, 0010)
cols = DCM_Img.get(0x00280011).value #Get number of cols from tag (0028, 0011)
Instance_Number = int(DCM_Img.get(0x00200013).value) #Get actual slice instance number from tag (0020, 0013)
Window_Center = int(DCM_Img.get(0x00281050).value) #Get window center from tag (0028, 1050)
Window_Width = int(DCM_Img.get(0x00281051).value) #Get window width from tag (0028, 1051)
Window_Max = int(Window_Center + Window_Width / 2)
Window_Min = int(Window_Center - Window_Width / 2)
if (DCM_Img.get(0x00281052) is None):
Rescale_Intercept = 0
else:
Rescale_Intercept = int(DCM_Img.get(0x00281052).value)
if (DCM_Img.get(0x00281053) is None):
Rescale_Slope = 1
else:
Rescale_Slope = int(DCM_Img.get(0x00281053).value)
New_Img = np.zeros((rows, cols), np.uint8)
Pixels = DCM_Img.pixel_array
for i in range(0, rows):
for j in range(0, cols):
Pix_Val = Pixels[i][j]
Rescale_Pix_Val = Pix_Val * Rescale_Slope + Rescale_Intercept
if (Rescale_Pix_Val > Window_Max): #if intensity is greater than max window
New_Img[i][j] = 255
elif (Rescale_Pix_Val < Window_Min): #if intensity is less than min window
New_Img[i][j] = 0
else:
New_Img[i][j] = int(((Rescale_Pix_Val - Window_Min) / (Window_Max - Window_Min)) * 255) #Normalize the intensities
return New_Img
file_path = "C:/example.dcm"
image = pydicom.read_file(file_path)
image = dicom_to_numpy(image)
#show image
cv2.imshow('sample image dicom',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
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')
I have opened a grayscale image using the Python Imaging Library, copied every pixel value into another image variable of same size and saved it. Now when I open the new image with an image viewer it looks reddish. I have used the Image.new() method with and without the "white" and "black" arguments got the same reddish output.
My code:
from PIL import Image
import math
def run():
im = Image.open("hrabowski.jpg")
pix = im.load()
print im.size
# print pix[0, 1]
im2 = Image.new("RGB", (2400, 2400))
for i in range(im.size[0]):
for j in range(im.size[0]):
im2.putpixel((i, j), pix[i, j])
im2.save("hrabowski-2400-2400.jpg")
Original image (scaled down to 500 x 500):
Python output of my code (scaled down to 500 x 500):
Could anyone please tell me what I am doing wrong?
Your problem is that you want to create an RGB image which has three channels. Therefore one pixel value consists of three values and not only one (in your case use the gray value of the original image for each of the channels).
I have modified the code accordingly.
A side remark: I am almost sure that there is a better way to do what you want to achieve, there is usually no need to loop through single pixels, but I am not sure what you are after.
from PIL import Image
import math
def run():
im = Image.open("ZHiG0.jpg")
pix = im.load()
print im.size
# print pix[0, 1]
im2 = Image.new("RGB", (2400, 2400))
for i in range(im.size[0]):
for j in range(im.size[0]):
im2.putpixel((i, j), (pix[i, j],pix[i, j],pix[i, j]))
im2.save("ZHiG0-2400-2400.jpg")
run()
I am loading my Image file using Image.open("image.tif"). Then i am using Image.load() to generate a pixelMap of the image. Then i am storing each pixel into an array. The following code describes this process. Then I want to create the ascii value of each pixel and store it in a string. So I going through each pixel in my pixel array and then change the pixel value to ascii value. However I am having an error because I am getting some pixel values greater than 250. How is this possible. Also, it is b/w image. What am I doing wrong?
self.filename = filename
self.im = Image.open(filename)
self.pixelmap = self.im.load() #Loads the image as a map of pixels
self.arr = []
for i in range(self.im.size[0]):
for j in range(self.im.size[1]):
mypixel = self.pixelmap[i, j]
self.arr.append(mypixel)
for i in msgFile.arr:
self.message += str(unichr(int(i)))
something like this?
from PIL import Image
import numpy as np
image = np.asarray(Image.open('image.jpg'))
_y, _x, _z = image.shape
str_array = [str(image[y][x]) for y in range(_y) for x in range(_x)]