Python Pillow Image.load() method has limitations? - python

I'm trying to do some image processing in python.
I'm using Pillow 8.4.0 for this purpose and I need to work on individual pixels (here I'm just trying to save pixels in a text file), therefore I'm trying to use Image.load() method and looping over it but it is throwing IndexError: image index out of range
Is there a limitation in Image.load() function that is preventing me to do this?
from PIL import Image
with Image.open('nature.jpg') as img:
print("Image size is : " ,img.size)
pixels = img.load()
with open('file.txt', 'w') as file:
for row in range(img.height):
for col in range(img.width):
file.write(str(pixels[row, col])+ ' ')
file.write('\n')
Output is:
Image size is : (1024, 768)
Traceback (most recent call last):
File "main.py", line 13, in <module>
file.write(str(pixels[row, col])+ ' ')
IndexError: image index out of range

Pillow expects (x,y) rather than (y,x). Please try following:
from PIL import Image
img = Image.open('nature.jpg')
pixels = img.load()
print(pixels[img.width-1,img.height-1]) # does provide tuple describing pixel
print(pixels[img.height-1,img.width-1]) # IndexError for non-square image

Related

Want to append colored images to a list and convert that list to grayscale using OpenCV

So basically I'm trying to convert a set of RGB images to grayscale using cv2.cvtColor and python is throwing the following error:
Traceback (most recent call last):
File "MCG.py", line 53, in
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
TypeError: src is not a numpy array, neither a scalar.
This here is the code:
import numpy as np
import cv2
import dlib
import sys
import skimage
from PIL import Image
import os
import glob
folderpath = sys.argv[1]
cascPath = sys.argv[2]
imageformat = ".tif"
path = folderpath
imfilelist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith(imageformat)]
data = []
for IMG in imfilelist:
print IMG
image = cv2.imread(IMG)
data.append(image)
cv2.imshow('Image', image)
cv2.waitKey(0)
faceCascade = cv2.CascadeClassifier(cascPath)
predictor = dlib.shape_predictor(PREDICTOR_PATH)
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.05,
minNeighbors=5,
minSize=(100,100)
)
As you can see, I'm trying to append all these images to a list, which will then be converted using the cv2.cvtColor function. However, that error is thrown. What am I doing wrong? Thank you.
P.S if anyone is wondering why I imported modules that don't seem to be used in this code, this code is just a segment of the whole thing and all of those modules have are being utilized in one way or the other.
If you read the cv2.cvtColor documentation, you can see that the first parameter is the Src 8-bit single channel image. However, in your case you are giving an entire list of images.
So change the code as
gray = []
for j in range(0,len(data)):
gray.append(cv2.cvtColor(np.array(data[j]), cv2.COLOR_BGR2GRAY))
I guess this should work.
You are collecting the images into a list with
data = []
for IMG in imfilelist:
...
data.append(image)
....
and then trying to convert the list with
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
This is why you are getting the error - the error is telling you that data is not an image (numpy array) but is a list. You need to convert one image at a time with cv2.cvtColor().
You could try
gray = []
for img in data:
gray.append(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
This would give you a list of greyscaled images, which is what I think you want to do.

error: (-2) could not find a writer for the specified extension in function imwrite_

I have data similar to the following :
10-0 = [
[1915, 387, 1933, 402],
[3350, 387, 3407, 391],
[842, 505, 863, 521],
]
10-0 being the credentials of an image(to find the path) and the values inside are coordinates of a box or rectangle each, the formula i'm working on is cv2.rectangle(img, (x1, y1), (x2, y2), (255,0,0), 2) , note that 10-0 is the key of one element of a dictionary.
My code :
import cv2
for key in my_dict:
folder_blueprint = re.findall(r'\d+', key)
img = PATH_TO_SOURCE+str(folder_blueprint[0])+'-'+str(folder_blueprint[1])+'.png'
for line in key:
line_number = 0
cv2.rectangle(img,( my_dict[key][line_number][0],my_dict[key][line_number][1]),(my_dict[key][line_number][2],my_dict[key][line_number][3]),(255,0,0),2)
# cv2.imread(img)
line_number = line_number + 1
cv2.imwrite(FULL_PATH_TO_DESTINATION, img)
cv2.imshow(FULL_PATH_TO_DESTINATION, img)
k = cv2.waitKey(0) # 0==wait forever
What i finally want is the image with red boxes around the regions of interest in a new destination folder leaving the original image intact.
And i have referred to similar questions on here with the same error message but they weren't helpful to my case.
EDIT: I adjusted the following :
img_path = PATH_TO_SOURCE+str(folder_blueprint[0])+'-'+str(folder_blueprint[1])+'.png'
img = cv2.imread(img_path)
and put int() where i had to in cv2.rectangle section and now i have this error error: (-2) could not find a writer for the specified extension in function imwrite_
Ensure that FULL_PATH_TO_DESTINATION has image extension also mentioned in the string of file name.
Read file has .png but I believe output path where you want to write image should have path as well filename with extention
give a read about cv2.rectangle here.
if you are drawing a rectangle, you should be passing a numpy array to rectangle function; then you can write your image to a specific path using imwrite.
I would assume you should have a for loop to iterate through all the images, the frame of which is a coloured rectangle.
an example here for converting an image to numpy array using PIL:
import numpy as np
from PIL import Image as im
# read image into a PIL data type: PIL.Image.Image, note it is not path/string.
img = im.open("sample.png").convert('RGBA')
# conver to numpy.nparray
arr = np.array(img)
# draw a rectangle on your existing image object
cv2.rectangle(img, (384,0),(510,128),(0,255,0),3)
# once you finished drawing, you can write it to a specific file.
cv2.imwrite("write_to_new_file.png", img)
you can have a look here how to use rectangle function and here how to write to a new file.
P.S. you can install PIL with following command in Python3:
pip3 install --upgrade image

openCV not accept numpy array?

I want to do a matchTemplate from a screenshot (with mss)
from mss import mss
import cv2
import numpy
with mss() as sct:
screenshot_numpy = numpy.array(sct.shot())
template = cv2.imread('./templates/player.png')
result = cv2.matchTemplate(screenshot_numpy,template,cv2.TM_CCOEFF_NORMED)
Error message:
Traceback (most recent call last):
File "main.py", line 14, in <module>
result = cv2.matchTemplate(screenshot_numpy,template,cv2.TM_CCOEFF_NORMED)
TypeError: image data type = 18 is not supported
From the mss examples page:
img = numpy.array(sct.grab(monitor))
So here we can see the .grab() method to get the raw pixel data from the image. In this case sct.grab() returns a PIL Image, and numpy.array(Image) will thus convert the PIL Image object into a numpy ndarray.
Check the numpy ndarray dtype after you convert; for e.g. if your code is ndarray_img = numpy.array(sct.grab()), then check ndarray_img.dtype. If it's np.uint8 then you're done. If it's np.uint16, then you'll have to divide by 256 and convert to np.uint8 with ndarray_img = (ndarray_img/255).astype(np.uint8).
Further down you'll see another example which flips the R and B channels of the image:
cv2.imshow(title, cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
except this is actually backwards. It really doesn't matter because either way it's just swapping the first and third channel, so BGR2RGB and RGB2BGR do exactly the same thing, but PIL (and other libraries) give you RGB order while you need BGR order to display with OpenCV, so technically it should be
cv2.imshow(title, cv2.cvtColor(img, cv2.COLOR_RGB2BGR))

Image module (from PIL) not recognizing mode = "HSV"

I'm trying to take my HSV values and make an image out of it. Here is my code:
from __future__ import division
from PIL import Image
import numpy as np
import colorsys
fp = open('pixels.txt', 'w')
fp2 = open('hsv.txt', 'w')
im = Image.open('colorimage.png')
imrgb = im.convert("RGB")
scale = 255.0
pixels = list(imrgb.getdata())
width, height = im.size
pixels = [pixels[i * width:(i + 1) * width] for i in xrange(height)]
for pixel in pixels:
for x in pixel:
print>>fp, x
x = [x[0]/255,x[1]/255,x[2]/255]
y = colorsys.rgb_to_hsv(*x)
w = [y[0]*360, y[1]*100, y[2]*100]
h,s,v = [y[0]*360, y[1]*100, y[2]*100]
print>>fp2, w
newimg = Image.new("HSV", im.size)
print "done"
The Image.new says it takes modes: http://pillow.readthedocs.io/en/4.0.x/handbook/concepts.html#concept-modes
But it doesn't read "HSV" as a mode. It says this as the error:
Traceback (most recent call last):
File "RGBtoHIS.py", line 25, in <module>
newimg = Image.new("HSV", im.size)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PIL-1.1.7-py2.7-macosx-10.6-x86_64.egg/PIL/Image.py", line 1763, in new
return Image()._new(core.fill(mode, size, color))
ValueError: unrecognized mode
Has anyone else had this issue with the Image module?
Other:
I would like to create a Hue image and a Saturation image. Is there a way to do this with the hue and saturation values I have?
You're referencing the Pillow docs, but you're not using Pillow -- you're using the original PIL version 1.1.7, as shown by your error message:
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/
site-packages/PIL-1.1.7-py2.7-macosx-10.6-x86_64.egg/PIL/Image.py", line 1763, in new
and according to its documentation, it doesn't support HSV as a mode (see here).
Uninstall PIL, install Pillow, and then you should be able to do
In [12]: PIL.__version__
Out[12]: '3.4.2'
In [13]: Image.new("HSV", (100,100))
Out[13]: <PIL.Image.Image image mode=HSV size=100x100 at 0x7F4FA00F4F60>

Copy a image on a new larger image

I have a color image sourceImage and I would copy this image on a new larger color image destImage: the source image should be centered over the new image. In order to perform this procedure, I wrote the following code:
destHeight: the height of the new larger image
destWidth: the width of the new larger image
sourceFilename: the path of the source image
sourceImage = cv2.imread(sourceFilename,1)
imgHeight, imgWidth, imgChannels = sourceImage.shape[:3]
#print sourceImage.shape[:3]
destImage = np.zeros((destHeight,destWidth,imgChannels), np.uint8)
#print destImage.shape[:3]
yBorder = (destHeight-imgHeight)/2
xBorder = (destWidth-imgWidth)/2
#print yBorder, xBorder
destImage[yBorder:imgHeight,xBorder:imgWidth] = sourceImage
cv2.imshow('dst', destImage)
cv2.waitKey(0)
But when I run the script, the python interpreter displays the following error:
Traceback (most recent call last):
File "examples.py", line 30, in <module>
destImage[yBorder:imgHeight,xBorder:imgWidth] = sourceImage
ValueError: shape mismatch: objects cannot be broadcast to a single shape
What is the reason for this error? How to solve it?
Try this:
destImage[yBorder:yBorder + imgHeight,xBorder:xBorder + imgWidth] = sourceImage
The slice syntax is start:stop, not start:width.

Categories